<commit_before>package typhon

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/facebookgo/httpcontrol"
	"github.com/fortytw2/leaktest"
	"github.com/monzo/terrors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestE2E(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	svc := Service(func(req Request) Response {
		// Simple requests like this shouldn't be chunked
		assert.NotContains(t, req.TransferEncoding, "chunked")
		assert.True(t, req.ContentLength > 0)
		return req.Response(map[string]string{
			"b": "a"})
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), map[string]string{
		"a": "b"})
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	require.NotNil(t, rsp.Request)
	assert.Equal(t, req, *rsp.Request)
	body := map[string]string{}
	assert.NoError(t, rsp.Decode(&body))
	assert.Equal(t, map[string]string{
		"b": "a"}, body)
	// The response is simple too; shouldn't be chunked
	assert.NotContains(t, rsp.TransferEncoding, "chunked")
	assert.True(t, rsp.ContentLength > 0)
}

func TestE2EDomainSocket(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	svc := Service(func(req Request) Response {
		return NewResponse(req)
	})
	svc = svc.Filter(ErrorFilter)

	addr := &net.UnixAddr{
		Net:  "unix",
		Name: "/tmp/typhon-test.sock"}
	l, err := net.ListenUnix("unix", addr)
	require.NoError(t, err)
	defer l.Close()

	s, err := Serve(svc, l)
	require.NoError(t, err)
	defer s.Stop()

	sockTransport := &httpcontrol.Transport{
		Dial: func(network, address string) (net.Conn, error) {
			return net.DialUnix("unix", nil, addr)
		}}
	req := NewRequest(ctx, "GET", "http://localhost/foo", nil)
	rsp := req.SendVia(HttpService(sockTransport)).Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
}

func TestE2EError(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	expectedErr := terrors.Unauthorized("ah_ah_ah", "You didn't say the magic word!", map[string]string{
		"param": "value"})
	svc := Service(func(req Request) Response {
		rsp := Response{
			Error: expectedErr}
		rsp.Write([]byte("throwaway")) // should be removed
		return rsp
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), nil)
	rsp := req.Send().Response()
	assert.Equal(t, http.StatusUnauthorized, rsp.StatusCode)

	b, _ := rsp.BodyBytes(false)
	assert.NotContains(t, string(b), "throwaway")

	require.Error(t, rsp.Error)
	terr := terrors.Wrap(rsp.Error, nil).(*terrors.Error)
	terrExpect := terrors.Unauthorized("ah_ah_ah", "You didn't say the magic word!", nil)
	assert.Equal(t, terrExpect.Message, terr.Message)
	assert.Equal(t, terrExpect.Code, terr.Code)
	assert.Equal(t, "value", terr.Params["param"])
}

func TestE2ECancellation(t *testing.T) {
	defer leaktest.Check(t)()

	cancelled := make(chan struct{})
	svc := Service(func(req Request) Response {
		<-req.Done()
		close(cancelled)
		return req.Response("cancelled ok")
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s/", s.Listener().Addr()), nil)
	req.Send()
	select {
	case <-cancelled:
		assert.Fail(t, "cancellation propagated prematurely")
	case <-time.After(30 * time.Millisecond):
	}
	cancel()
	select {
	case <-cancelled:
	case <-time.After(30 * time.Millisecond):
		assert.Fail(t, "cancellation not propagated")
	}
}

func TestNoFollowRedirect(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	svc := Service(func(req Request) Response {
		if req.URL.Path == "/redirected" {
			return req.Response("😱")
		}

		rsp := req.Response(nil)
		dst := fmt.Sprintf("http://%s/redirected", req.Host)
		http.Redirect(rsp.Writer(), &req.Request, dst, http.StatusFound)
		return rsp
	})
	s := serve(t, svc)
	defer s.Stop()
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s/", s.Listener().Addr()), nil)
	rsp := req.Send().Response()
	assert.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusFound, rsp.StatusCode)
}

func TestProxiedStreamer(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	chunks := make(chan bool, 2)
	chunks <- true
	downstream := Service(func(req Request) Response {
		rsp := req.Response(nil)
		rsp.Body = Streamer()
		go func() {
			defer rsp.Body.Close()
			n := 0
			for range chunks {
				rsp.Encode(map[string]int{
					"chunk": n})
				n++
			}
		}()
		return rsp
	})
	s := serve(t, downstream)
	defer s.Stop()

	proxy := Service(func(req Request) Response {
		proxyReq := NewRequest(req, "GET", fmt.Sprintf("http://%s/", s.Listener().Addr()), nil)
		return proxyReq.Send().Response()
	})
	ps := serve(t, proxy)
	defer ps.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s/", ps.Listener().Addr()), nil)
	rsp := req.Send().Response()
	assert.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	// The response is streaming; should be chunked
	assert.Contains(t, rsp.TransferEncoding, "chunked")
	assert.True(t, rsp.ContentLength < 0)
	for i := 0; i < 1000; i++ {
		b := make([]byte, 500)
		n, err := rsp.Body.Read(b)
		require.NoError(t, err)
		v := map[string]int{}
		require.NoError(t, json.Unmarshal(b[:n], &v))
		require.Equal(t, i, v["chunk"])
		chunks <- true
	}
	close(chunks)
}

// TestInfiniteContext verifies that Typhon does not leak Goroutines if an infinite context (one that's never cancelled)
// is used to make a request.
func TestInfiniteContext(t *testing.T) {
	defer leaktest.Check(t)()
	ctx := context.Background()

	var receivedCtx context.Context
	svc := Service(func(req Request) Response {
		receivedCtx = req.Context
		return req.Response(map[string]string{
			"b": "a"})
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), map[string]string{
		"a": "b"})
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)

	b, err := ioutil.ReadAll(rsp.Body)
	require.NoError(t, err)
	assert.Equal(t, "{\"b\":\"a\"}\n", string(b))

	// Consuming the body should have closed the receiving context
	select {
	case <-receivedCtx.Done():
	case <-time.After(time.Second):
		assert.Fail(t, "cancellation not propagated")
	}
}

func TestRequestAutoChunking(t *testing.T) {
	defer leaktest.Check(t)()
	receivedChunked := false
	svc := Service(func(req Request) Response {
		receivedChunked = false
		for _, e := range req.TransferEncoding {
			if e == "chunked" {
				receivedChunked = true
			}
		}
		return req.Response("ok")
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Streamer; should be chunked
	stream := Streamer()
	go func() {
		io.WriteString(stream, "foo\n")
		stream.Close()
	}()
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), nil)
	req.Body = stream
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.True(t, receivedChunked)

	// Small request using Encode(): should not be chunked
	req = NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), map[string]string{
		"a": "b"})
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.False(t, receivedChunked)

	// Large request using Encode(); should be chunked
	const targetBytes = 5000000 // 5 MB
	body := []byte{}
	for len(body) < targetBytes {
		body = append(body, []byte("abc=def\n")...)
	}
	req = NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), body)
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.True(t, receivedChunked)
}

func TestResponseAutoChunking(t *testing.T) {
	defer leaktest.Check(t)()
	var sendRsp Response
	svc := Service(func(req Request) Response {
		sendRsp.Request = &req
		return sendRsp
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Streamer; should be chunked
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), nil)
	sendRsp = NewResponse(req)
	stream := Streamer()
	go func() {
		io.WriteString(stream, "foo\n")
		stream.Close()
	}()
	sendRsp.Body = stream
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.Contains(t, rsp.TransferEncoding, "chunked")

	// Small request using Encode(): should not be chunked
	sendRsp = NewResponse(req)
	sendRsp.Encode(map[string]string{
		"a": "b"})
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.NotContains(t, rsp.TransferEncoding, "chunked")

	// Large request using Encode(); should be chunked
	const targetBytes = 5000000 // 5 MB
	body := []byte{}
	for len(body) < targetBytes {
		body = append(body, []byte("abc=def\n")...)
	}
	sendRsp = NewResponse(req)
	sendRsp.Encode(body)
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.Contains(t, rsp.TransferEncoding, "chunked")
}

}

func BenchmarkRequestResponse(b *testing.B) {
	b.ReportAllocs()
	svc := Service(func(req Request) Response {
		rsp := req.Response(nil)
		rsp.Header.Set("a", "b")
		rsp.Header.Set("b", "b")
		rsp.Header.Set("c", "b")
		return rsp
	})
	addr := &net.UnixAddr{
		Net:  "unix",
		Name: "/tmp/typhon-test.sock"}
	l, _ := net.ListenUnix("unix", addr)
	defer l.Close()
	s, _ := Serve(svc, l)
	defer s.Stop()

	sockTransport := &httpcontrol.Transport{
		Dial: func(network, address string) (net.Conn, error) {
			return net.DialUnix("unix", nil, addr)
		}}

	ctx := context.Background()
	req := NewRequest(ctx, "GET", "http://localhost/foo", nil)
	sockSvc := HttpService(sockTransport)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		req.SendVia(sockSvc).Response()
	}
}
<commit_msg>Test streamer behaviour (writing chunks) after client cancellation<commit_after>package typhon

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"testing"
	"time"

	"github.com/facebookgo/httpcontrol"
	"github.com/fortytw2/leaktest"
	"github.com/monzo/terrors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestE2E(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	svc := Service(func(req Request) Response {
		// Simple requests like this shouldn't be chunked
		assert.NotContains(t, req.TransferEncoding, "chunked")
		assert.True(t, req.ContentLength > 0)
		return req.Response(map[string]string{
			"b": "a"})
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), map[string]string{
		"a": "b"})
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	require.NotNil(t, rsp.Request)
	assert.Equal(t, req, *rsp.Request)
	body := map[string]string{}
	assert.NoError(t, rsp.Decode(&body))
	assert.Equal(t, map[string]string{
		"b": "a"}, body)
	// The response is simple too; shouldn't be chunked
	assert.NotContains(t, rsp.TransferEncoding, "chunked")
	assert.True(t, rsp.ContentLength > 0)
}

func TestE2EDomainSocket(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	svc := Service(func(req Request) Response {
		return NewResponse(req)
	})
	svc = svc.Filter(ErrorFilter)

	addr := &net.UnixAddr{
		Net:  "unix",
		Name: "/tmp/typhon-test.sock"}
	l, err := net.ListenUnix("unix", addr)
	require.NoError(t, err)
	defer l.Close()

	s, err := Serve(svc, l)
	require.NoError(t, err)
	defer s.Stop()

	sockTransport := &httpcontrol.Transport{
		Dial: func(network, address string) (net.Conn, error) {
			return net.DialUnix("unix", nil, addr)
		}}
	req := NewRequest(ctx, "GET", "http://localhost/foo", nil)
	rsp := req.SendVia(HttpService(sockTransport)).Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
}

func TestE2EError(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	expectedErr := terrors.Unauthorized("ah_ah_ah", "You didn't say the magic word!", map[string]string{
		"param": "value"})
	svc := Service(func(req Request) Response {
		rsp := Response{
			Error: expectedErr}
		rsp.Write([]byte("throwaway")) // should be removed
		return rsp
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), nil)
	rsp := req.Send().Response()
	assert.Equal(t, http.StatusUnauthorized, rsp.StatusCode)

	b, _ := rsp.BodyBytes(false)
	assert.NotContains(t, string(b), "throwaway")

	require.Error(t, rsp.Error)
	terr := terrors.Wrap(rsp.Error, nil).(*terrors.Error)
	terrExpect := terrors.Unauthorized("ah_ah_ah", "You didn't say the magic word!", nil)
	assert.Equal(t, terrExpect.Message, terr.Message)
	assert.Equal(t, terrExpect.Code, terr.Code)
	assert.Equal(t, "value", terr.Params["param"])
}

func TestE2ECancellation(t *testing.T) {
	defer leaktest.Check(t)()

	cancelled := make(chan struct{})
	svc := Service(func(req Request) Response {
		<-req.Done()
		close(cancelled)
		return req.Response("cancelled ok")
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s/", s.Listener().Addr()), nil)
	req.Send()
	select {
	case <-cancelled:
		assert.Fail(t, "cancellation propagated prematurely")
	case <-time.After(30 * time.Millisecond):
	}
	cancel()
	select {
	case <-cancelled:
	case <-time.After(30 * time.Millisecond):
		assert.Fail(t, "cancellation not propagated")
	}
}

func TestNoFollowRedirect(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	svc := Service(func(req Request) Response {
		if req.URL.Path == "/redirected" {
			return req.Response("😱")
		}

		rsp := req.Response(nil)
		dst := fmt.Sprintf("http://%s/redirected", req.Host)
		http.Redirect(rsp.Writer(), &req.Request, dst, http.StatusFound)
		return rsp
	})
	s := serve(t, svc)
	defer s.Stop()
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s/", s.Listener().Addr()), nil)
	rsp := req.Send().Response()
	assert.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusFound, rsp.StatusCode)
}

func TestProxiedStreamer(t *testing.T) {
	defer leaktest.Check(t)()
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	chunks := make(chan bool, 2)
	chunks <- true
	downstream := Service(func(req Request) Response {
		rsp := req.Response(nil)
		rsp.Body = Streamer()
		go func() {
			defer rsp.Body.Close()
			n := 0
			for range chunks {
				rsp.Encode(map[string]int{
					"chunk": n})
				n++
			}
		}()
		return rsp
	})
	s := serve(t, downstream)
	defer s.Stop()

	proxy := Service(func(req Request) Response {
		proxyReq := NewRequest(req, "GET", fmt.Sprintf("http://%s/", s.Listener().Addr()), nil)
		return proxyReq.Send().Response()
	})
	ps := serve(t, proxy)
	defer ps.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s/", ps.Listener().Addr()), nil)
	rsp := req.Send().Response()
	assert.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	// The response is streaming; should be chunked
	assert.Contains(t, rsp.TransferEncoding, "chunked")
	assert.True(t, rsp.ContentLength < 0)
	for i := 0; i < 1000; i++ {
		b := make([]byte, 500)
		n, err := rsp.Body.Read(b)
		require.NoError(t, err)
		v := map[string]int{}
		require.NoError(t, json.Unmarshal(b[:n], &v))
		require.Equal(t, i, v["chunk"])
		chunks <- true
	}
	close(chunks)
}

// TestInfiniteContext verifies that Typhon does not leak Goroutines if an infinite context (one that's never cancelled)
// is used to make a request.
func TestInfiniteContext(t *testing.T) {
	defer leaktest.Check(t)()
	ctx := context.Background()

	var receivedCtx context.Context
	svc := Service(func(req Request) Response {
		receivedCtx = req.Context
		return req.Response(map[string]string{
			"b": "a"})
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), map[string]string{
		"a": "b"})
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)

	b, err := ioutil.ReadAll(rsp.Body)
	require.NoError(t, err)
	assert.Equal(t, "{\"b\":\"a\"}\n", string(b))

	// Consuming the body should have closed the receiving context
	select {
	case <-receivedCtx.Done():
	case <-time.After(time.Second):
		assert.Fail(t, "cancellation not propagated")
	}
}

func TestRequestAutoChunking(t *testing.T) {
	defer leaktest.Check(t)()
	receivedChunked := false
	svc := Service(func(req Request) Response {
		receivedChunked = false
		for _, e := range req.TransferEncoding {
			if e == "chunked" {
				receivedChunked = true
			}
		}
		return req.Response("ok")
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Streamer; should be chunked
	stream := Streamer()
	go func() {
		io.WriteString(stream, "foo\n")
		stream.Close()
	}()
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), nil)
	req.Body = stream
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.True(t, receivedChunked)

	// Small request using Encode(): should not be chunked
	req = NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), map[string]string{
		"a": "b"})
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.False(t, receivedChunked)

	// Large request using Encode(); should be chunked
	const targetBytes = 5000000 // 5 MB
	body := []byte{}
	for len(body) < targetBytes {
		body = append(body, []byte("abc=def\n")...)
	}
	req = NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), body)
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.True(t, receivedChunked)
}

func TestResponseAutoChunking(t *testing.T) {
	defer leaktest.Check(t)()
	var sendRsp Response
	svc := Service(func(req Request) Response {
		sendRsp.Request = &req
		return sendRsp
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Streamer; should be chunked
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s", s.Listener().Addr()), nil)
	sendRsp = NewResponse(req)
	stream := Streamer()
	go func() {
		io.WriteString(stream, "foo\n")
		stream.Close()
	}()
	sendRsp.Body = stream
	rsp := req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.Contains(t, rsp.TransferEncoding, "chunked")

	// Small request using Encode(): should not be chunked
	sendRsp = NewResponse(req)
	sendRsp.Encode(map[string]string{
		"a": "b"})
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.NotContains(t, rsp.TransferEncoding, "chunked")

	// Large request using Encode(); should be chunked
	const targetBytes = 5000000 // 5 MB
	body := []byte{}
	for len(body) < targetBytes {
		body = append(body, []byte("abc=def\n")...)
	}
	sendRsp = NewResponse(req)
	sendRsp.Encode(body)
	rsp = req.Send().Response()
	require.NoError(t, rsp.Error)
	assert.Equal(t, http.StatusOK, rsp.StatusCode)
	assert.Contains(t, rsp.TransferEncoding, "chunked")
}

// TestStreamingCancellation asserts that a server's writes won't block forever if a client cancels a request
func TestStreamingCancellation(t *testing.T) {
	defer leaktest.Check(t)()

	done := make(chan struct{})
	svc := Service(func(req Request) Response {
		s := Streamer()
		go func() {
			defer close(done)
			io.WriteString(s, "derp\n")
			<-req.Done()
			// Write a bunch of chunks; this should not block forever even though the client has gone
			for i := 0; i < 500; i++ {
				io.WriteString(s, "derp\n")
			}
		}()
		rsp := req.Response(nil)
		rsp.Body = s
		return rsp
	})
	svc = svc.Filter(ErrorFilter)
	s := serve(t, svc)
	defer s.Stop()

	ctx, cancel := context.WithCancel(context.Background())
	req := NewRequest(ctx, "GET", fmt.Sprintf("http://%s/", s.Listener().Addr()), nil)
	req.Send().Response()
	cancel()
	<-done
}

func BenchmarkRequestResponse(b *testing.B) {
	b.ReportAllocs()
	svc := Service(func(req Request) Response {
		rsp := req.Response(nil)
		rsp.Header.Set("a", "b")
		rsp.Header.Set("b", "b")
		rsp.Header.Set("c", "b")
		return rsp
	})
	addr := &net.UnixAddr{
		Net:  "unix",
		Name: "/tmp/typhon-test.sock"}
	l, _ := net.ListenUnix("unix", addr)
	defer l.Close()
	s, _ := Serve(svc, l)
	defer s.Stop()

	sockTransport := &httpcontrol.Transport{
		Dial: func(network, address string) (net.Conn, error) {
			return net.DialUnix("unix", nil, addr)
		}}

	ctx := context.Background()
	req := NewRequest(ctx, "GET", "http://localhost/foo", nil)
	sockSvc := HttpService(sockTransport)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		req.SendVia(sockSvc).Response()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"sync"
	"time"
)

type trace struct {
	Path     string
	Start    time.Time
	Duration float64
	Status   int
}

type traceList struct {
	createdAt time.Time
	list      []trace
}

func newTraceList() *traceList {
	return &traceList{
		createdAt: time.Now(),
		list:      make([]trace, 0, maxTraces),
	}
}

type tracer struct {
	sync.Mutex
	handler   http.Handler
	traceList *traceList
}

type statusRecorder struct {
	http.ResponseWriter
	status int
}

func (sr *statusRecorder) WriteHeader(code int) {
	sr.status = code
	sr.ResponseWriter.WriteHeader(code)
}

// ServeHTTP implements http.Handler.  We call the underlying handler and store
// timing data locally.  If we have enough timing data, we send it off to be
// written to disk.
func (t *tracer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var now = time.Now()
	var sr = statusRecorder{w, 200}
	t.handler.ServeHTTP(&sr, req)
	go t.appendTrace(req.URL.Path, now, time.Since(now), sr.status)
}

func (t *tracer) appendTrace(path string, start time.Time, duration time.Duration, status int) {
	t.Lock()
	defer t.Unlock()

	t.traceList.list = append(t.traceList.list, trace{
		Path:     path,
		Start:    start,
		Duration: duration.Seconds(),
		Status:   status,
	})

	if len(t.traceList.list) >= maxTraces || time.Since(t.traceList.createdAt) > flushTime {
		var oldList = t.traceList
		t.traceList = newTraceList()
		writeTraces(oldList.list)
	}
}

func (t *tracer) shutdown(wg *sync.WaitGroup) {
	t.Lock()
	writeTraces(t.traceList.list)
	t.Unlock()
	wg.Done()
}

type registry struct {
	sync.Mutex
	list []*tracer
}

func (r *registry) new(h http.Handler) *tracer {
	var t = &tracer{handler: h, traceList: newTraceList()}
	r.Lock()
	r.list = append(r.list, t)
	r.Unlock()
	return t
}

func (r *registry) shutdown() {
	r.Lock()
	defer r.Unlock()

	var wg sync.WaitGroup
	for _, t := range r.list {
		wg.Add(1)
		go t.shutdown(&wg)
	}

	wg.Wait()
}
<commit_msg>plugins/json-tracer: improve reported path<commit_after>package main

import (
	"net/http"
	"sync"
	"time"
)

type trace struct {
	Path     string
	Start    time.Time
	Duration float64
	Status   int
}

type traceList struct {
	createdAt time.Time
	list      []trace
}

func newTraceList() *traceList {
	return &traceList{
		createdAt: time.Now(),
		list:      make([]trace, 0, maxTraces),
	}
}

type tracer struct {
	sync.Mutex
	handler   http.Handler
	traceList *traceList
}

type statusRecorder struct {
	http.ResponseWriter
	status int
}

func (sr *statusRecorder) WriteHeader(code int) {
	sr.status = code
	sr.ResponseWriter.WriteHeader(code)
}

// ServeHTTP implements http.Handler.  We call the underlying handler and store
// timing data locally.  If we have enough timing data, we send it off to be
// written to disk.
func (t *tracer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	var now = time.Now()
	var sr = statusRecorder{w, 200}
	t.handler.ServeHTTP(&sr, req)
	var path = req.URL.RawPath
	if path == "" {
		path = req.URL.Path
	}
	go t.appendTrace(path, now, time.Since(now), sr.status)
}

func (t *tracer) appendTrace(path string, start time.Time, duration time.Duration, status int) {
	t.Lock()
	defer t.Unlock()

	t.traceList.list = append(t.traceList.list, trace{
		Path:     path,
		Start:    start,
		Duration: duration.Seconds(),
		Status:   status,
	})

	if len(t.traceList.list) >= maxTraces || time.Since(t.traceList.createdAt) > flushTime {
		var oldList = t.traceList
		t.traceList = newTraceList()
		writeTraces(oldList.list)
	}
}

func (t *tracer) shutdown(wg *sync.WaitGroup) {
	t.Lock()
	writeTraces(t.traceList.list)
	t.Unlock()
	wg.Done()
}

type registry struct {
	sync.Mutex
	list []*tracer
}

func (r *registry) new(h http.Handler) *tracer {
	var t = &tracer{handler: h, traceList: newTraceList()}
	r.Lock()
	r.list = append(r.list, t)
	r.Unlock()
	return t
}

func (r *registry) shutdown() {
	r.Lock()
	defer r.Unlock()

	var wg sync.WaitGroup
	for _, t := range r.list {
		wg.Add(1)
		go t.shutdown(&wg)
	}

	wg.Wait()
}
<|endoftext|>


<commit_before>// Copyright 2012 Michael Meier. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package i2cm

import (
	"errors"
	"fmt"
	"io"
	"time"
)

const (
	MAX_EEPROM_SIZE = 1 << (16 + 3)
)

// EEPROM24Config is used to configure the EEPROM driver to use a
// specific device.
type EEPROM24Config struct {
	Size       uint
	PageSize   uint
	WriteDelay time.Duration // time to wait after a page write. Address polling is not implemented
}

var Conf_24C02 = EEPROM24Config{256, 8, 5 * time.Millisecond}
var Conf_24C128 = EEPROM24Config{16384, 64, 5 * time.Millisecond}

// ee24 supports EEPROMs which require an 8 bit word
// address. IIRC, this includes types up to 24c16.
// 24c32 devices and up require a 16 word address.
type ee24 struct {
	conf    EEPROM24Config
	m       I2CMaster
	tr      Transactor
	p       uint // file pointer
	devaddr Addr
}

// EEPROM24 represents an I2C EEPROM device. The memory array is made
// available via a file-like interface. The file's size is fixed to
// the memory array size and writes past the end of the array result
// in an error.
type EEPROM24 interface {
	io.Reader
	io.Seeker
	io.Writer
}

func ispow2(i uint64) bool {
	for (i&0x01) == 0 && i > 0 {
		i >>= 1
	}
	return i == 1
}

// NewEEPROM24 constructs an I2C EEPROM driver for a device with base
// address devaddr residing on m's bus. The EEPROM driver parameters
// are passed in conf. Invalid configurations are rejected.
//
// The EEPROM driver currently uses Transactor8x8 and thus only works
// correctly for devices with 8 bit register addresses.
func NewEEPROM24(m I2CMaster, devaddr Addr, conf EEPROM24Config) (EEPROM24, error) {
	if conf.PageSize > conf.Size {
		return nil, errors.New("EEPROM24: page size needs to be smaller than array size")
	}

	if conf.Size > MAX_EEPROM_SIZE {
		return nil, fmt.Errorf("EEPROM24: invalid size in configuration. passed %d bytes, a maximum of %d bytes are supported", conf.Size, MAX_EEPROM_SIZE)
	}

	if !ispow2(uint64(conf.Size)) {
		return nil, errors.New("EEPROM24: array size needs to be a power of 2")
	}

	if !ispow2(uint64(conf.PageSize)) {
		return nil, errors.New("EEPROM24: page size needs to be a power of 2")
	}

	if devaddr.GetAddrLen() != 7 {
		return nil, errors.New("only EEPROMs with 7 bit device addresses are supported")
	}

	var e ee24

	e.m = m
	e.tr = NewTransactor(m)
	e.conf = conf
	e.p = 0
	e.devaddr = devaddr

	return &e, nil
}

// hasSmallAddress returs true if the EEPROM config in question uses the small,
// i.e. the 8 bit + 3 bit, addressing convention. EEPROMs 24c16 and below
// use the small addressing convention, 24c32 and up use the big addressing
// convention, i.e. the 16 bit + 3 bit one.
func (e EEPROM24Config) hasSmallAddresses() bool {
	if e.Size <= (1 << 11) {
		return true
	}
	return false
}

func (e *ee24) Read(b []byte) (int, error) {
	// TODO: does read address roll over at the end of the
	// memory array or every 256 bytes?

	startpos := e.p
	endpos := startpos + uint(len(b))
	if endpos > e.conf.Size {
		endpos = e.conf.Size
	}

	if endpos-startpos == 0 {
		return 0, io.EOF
	}

	rb := b[0:(endpos - startpos)]
	var nr int
	var err error

	// devaddrinc is protected from overflow by the read/write/seek logic
	// more protection might still be desirable though
	if e.conf.hasSmallAddresses() {
		devaddrinc := startpos >> 8 // 256 byte every 1 7-bit slave addr
		devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

		regaddr := uint8(startpos & 0xff)

		_, nr, err = e.tr.Transact8x8(devaddr, regaddr, nil, rb)
	} else {
		devaddrinc := startpos >> 16 // 256 bytes every 1 7-bit slave addr
		devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

		regaddr := uint16(startpos)

		_, nr, err = e.tr.Transact16x8(devaddr, regaddr, nil, rb)
	}

	e.p += uint(nr)

	return nr, err
}

func (e *ee24) Seek(offset int64, whence int) (int64, error) {
	P := int64(e.p)

	// this may fail in funny ways for big absolute values of offset.
	var nP int64
	switch whence {
	case 0:
		nP = offset
	case 1:
		nP = P + offset
	case 2:
		nP = int64(e.conf.Size) + offset
	default:
		return P, errors.New("EEPROM24.Seek: invalid whence")
	}

	if nP < 0 {
		return P, errors.New("EEPROM24.Seek: negative position")
	}

	if nP > int64(e.conf.Size) {
		return P, errors.New("EEPROM24.Seek: desired position beyond end of EEPROM array")
	}

	e.p = uint(nP)

	return P, nil
}

func (e *ee24) Write(b []byte) (int, error) {
	origsize := len(b)

	for len(b) > 0 && e.p < e.conf.Size {

		// address in page
		aip := e.p & (e.conf.PageSize - 1)
		//log.Printf("e.p %#04x  aip %#02x\n", e.p, aip)
		// get number of bytes to write in this page
		nip := uint(len(b))
		if nip > e.conf.PageSize-aip {
			nip = e.conf.PageSize - aip
		}

		// do transaction
		//log.Printf("at p %#04x, pagesize %#02x read nip %#02x\n", e.p, e.PageSize, nip)
		var nw int
		var err error

		if e.conf.hasSmallAddresses() {
			regaddr := uint8(e.p & 0xff)
			devaddrinc := e.p >> 8 // 256 byte every 1 7-bit slave addr
			devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

			nw, _, err = e.tr.Transact8x8(devaddr, regaddr, b[0:nip], nil)
		} else {
			regaddr := uint16(e.p)
			devaddrinc := e.p >> 16 // 256 bytes every 1 7-bit slave addr
			devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

			nw, _, err = e.tr.Transact16x8(devaddr, regaddr, b[0:nip], nil)
		}

		if err != nil {
			return origsize - len(b) + nw, err
		}

		// TODO: either wait or poll for device

		e.p += uint(nip)
		b = b[nip:]
	}

	//log.Printf("at end of write, p %d  len(b) %d\n", e.p, len(b))

	if e.p == e.conf.Size {
		// reached the end of the array
		if len(b) > 0 {
			return origsize - len(b), io.EOF
		}
	}
	if e.p > e.conf.Size {
		panic("wrote beyond end of EEPROM. is the configuration correct?")
	}

	return origsize, nil
}
<commit_msg>update doc<commit_after>// Copyright 2012 Michael Meier. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package i2cm

import (
	"errors"
	"fmt"
	"io"
	"time"
)

const (
	MAX_EEPROM_SIZE = 1 << (16 + 3)
)

// EEPROM24Config is used to configure the EEPROM driver to use a
// specific device. There are two protocols in use with 24Cxx devices:
// 24C16 and smaller are addressed with 8+3 bits: 8 bits in the "register
// address" following the device address on the bus and up to 3 bits in the
// device address. 24C32 and larger are addressed with 16+3 bits: 16 bits in
// the two bytes following the device address and up to 3 bits in the device
// address. NewEEPROM24 switches between the protocols based on the above
// criteria.
type EEPROM24Config struct {
	Size       uint
	PageSize   uint
	WriteDelay time.Duration // time to wait after a page write. Address polling is not implemented
}

var Conf_24C02 = EEPROM24Config{256, 8, 5 * time.Millisecond}
var Conf_24C128 = EEPROM24Config{16384, 64, 5 * time.Millisecond}

// ee24 supports 24Cxx family EEPROMs, both the 8+3 bit addressed
// (24c16 and below) and the 16+3 bit addressed (24c32 and up) kind.
// ee24 switches between the addressing modes at runtime, c.f. hasSmallAddresses.
type ee24 struct {
	conf    EEPROM24Config
	m       I2CMaster
	tr      Transactor
	p       uint // file pointer
	devaddr Addr
}

// EEPROM24 represents an I2C EEPROM device. The memory array is made
// available via a file-like interface. The file's size is fixed to
// the memory array size and writes past the end of the array result
// in an error.
type EEPROM24 interface {
	io.Reader
	io.Seeker
	io.Writer
}

func ispow2(i uint64) bool {
	for (i&0x01) == 0 && i > 0 {
		i >>= 1
	}
	return i == 1
}

// NewEEPROM24 constructs an I2C EEPROM driver for a device with base
// address devaddr residing on m's bus. The EEPROM driver parameters
// are passed in conf. Invalid configurations are rejected.
func NewEEPROM24(m I2CMaster, devaddr Addr, conf EEPROM24Config) (EEPROM24, error) {
	if conf.PageSize > conf.Size {
		return nil, errors.New("EEPROM24: page size needs to be smaller than array size")
	}

	if conf.Size > MAX_EEPROM_SIZE {
		return nil, fmt.Errorf("EEPROM24: invalid size in configuration. passed %d bytes, a maximum of %d bytes are supported", conf.Size, MAX_EEPROM_SIZE)
	}

	if !ispow2(uint64(conf.Size)) {
		return nil, errors.New("EEPROM24: array size needs to be a power of 2")
	}

	if !ispow2(uint64(conf.PageSize)) {
		return nil, errors.New("EEPROM24: page size needs to be a power of 2")
	}

	if devaddr.GetAddrLen() != 7 {
		return nil, errors.New("only EEPROMs with 7 bit device addresses are supported")
	}

	var e ee24

	e.m = m
	e.tr = NewTransactor(m)
	e.conf = conf
	e.p = 0
	e.devaddr = devaddr

	return &e, nil
}

// hasSmallAddress returs true if the EEPROM config in question uses the small,
// i.e. the 8 bit + 3 bit, addressing convention. EEPROMs 24c16 and below
// use the small addressing convention, 24c32 and up use the big addressing
// convention, i.e. the 16 bit + 3 bit one.
func (e EEPROM24Config) hasSmallAddresses() bool {
	if e.Size <= (1 << 11) {
		return true
	}
	return false
}

func (e *ee24) Read(b []byte) (int, error) {
	// TODO: does read address roll over at the end of the
	// memory array or every 256 bytes?

	startpos := e.p
	endpos := startpos + uint(len(b))
	if endpos > e.conf.Size {
		endpos = e.conf.Size
	}

	if endpos-startpos == 0 {
		return 0, io.EOF
	}

	rb := b[0:(endpos - startpos)]
	var nr int
	var err error

	// devaddrinc is protected from overflow by the read/write/seek logic
	// more protection might still be desirable though
	if e.conf.hasSmallAddresses() {
		devaddrinc := startpos >> 8 // 256 byte every 1 7-bit slave addr
		devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

		regaddr := uint8(startpos & 0xff)

		_, nr, err = e.tr.Transact8x8(devaddr, regaddr, nil, rb)
	} else {
		devaddrinc := startpos >> 16 // 256 bytes every 1 7-bit slave addr
		devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

		regaddr := uint16(startpos)

		_, nr, err = e.tr.Transact16x8(devaddr, regaddr, nil, rb)
	}

	e.p += uint(nr)

	return nr, err
}

func (e *ee24) Seek(offset int64, whence int) (int64, error) {
	P := int64(e.p)

	// this may fail in funny ways for big absolute values of offset.
	var nP int64
	switch whence {
	case 0:
		nP = offset
	case 1:
		nP = P + offset
	case 2:
		nP = int64(e.conf.Size) + offset
	default:
		return P, errors.New("EEPROM24.Seek: invalid whence")
	}

	if nP < 0 {
		return P, errors.New("EEPROM24.Seek: negative position")
	}

	if nP > int64(e.conf.Size) {
		return P, errors.New("EEPROM24.Seek: desired position beyond end of EEPROM array")
	}

	e.p = uint(nP)

	return P, nil
}

func (e *ee24) Write(b []byte) (int, error) {
	origsize := len(b)

	for len(b) > 0 && e.p < e.conf.Size {

		// address in page
		aip := e.p & (e.conf.PageSize - 1)
		//log.Printf("e.p %#04x  aip %#02x\n", e.p, aip)
		// get number of bytes to write in this page
		nip := uint(len(b))
		if nip > e.conf.PageSize-aip {
			nip = e.conf.PageSize - aip
		}

		// do transaction
		//log.Printf("at p %#04x, pagesize %#02x read nip %#02x\n", e.p, e.PageSize, nip)
		var nw int
		var err error

		if e.conf.hasSmallAddresses() {
			regaddr := uint8(e.p & 0xff)
			devaddrinc := e.p >> 8 // 256 byte every 1 7-bit slave addr
			devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

			nw, _, err = e.tr.Transact8x8(devaddr, regaddr, b[0:nip], nil)
		} else {
			regaddr := uint16(e.p)
			devaddrinc := e.p >> 16 // 256 bytes every 1 7-bit slave addr
			devaddr := Addr7(uint8(e.devaddr.GetBaseAddr() + uint16(devaddrinc)))

			nw, _, err = e.tr.Transact16x8(devaddr, regaddr, b[0:nip], nil)
		}

		if err != nil {
			return origsize - len(b) + nw, err
		}

		// TODO: either wait or poll for device

		e.p += uint(nip)
		b = b[nip:]
	}

	//log.Printf("at end of write, p %d  len(b) %d\n", e.p, len(b))

	if e.p == e.conf.Size {
		// reached the end of the array
		if len(b) > 0 {
			return origsize - len(b), io.EOF
		}
	}
	if e.p > e.conf.Size {
		panic("wrote beyond end of EEPROM. is the configuration correct?")
	}

	return origsize, nil
}
<|endoftext|>


<commit_before>package respond

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

	"sync"
)

// Encoder descirbes an object capable of encoding
// a response.
type Encoder interface {
	Encode(w http.ResponseWriter, r *http.Request, v interface{}) error
	ContentType(w http.ResponseWriter, r *http.Request) string
}

// DefaultEncoder is the Encoder to use if no other can
// be matched.
var DefaultEncoder = JSON

var encoders = &encodersMap{
	encoders: map[string]Encoder{"json": JSON},
}

// Encoders allows the adding, removing and matching of encoders
// by fuzzy strings, usually the Accept request header.
// By default, JSON is added.
func Encoders() interface {
	Add(match string, encoder Encoder)
	Match(s string) (Encoder, bool)
	Del(e Encoder)
} {
	return encoders
}

type encodersMap struct {
	lock     sync.RWMutex
	encoders map[string]Encoder
}

func (m *encodersMap) Add(match string, e Encoder) {
	m.lock.Lock()
	m.encoders[match] = e
	m.lock.Unlock()
}

func (m *encodersMap) Del(e Encoder) {
	m.lock.Lock()
	defer m.lock.Unlock()
	found := false
	for k, v := range m.encoders {
		if v == e {
			delete(m.encoders, k)
			found = true
		}
	}
	if !found {
		panic("respond: encoder not found")
	}
}

func (m *encodersMap) Match(s string) (Encoder, bool) {
	m.lock.RLock()
	defer m.lock.RUnlock()
	for k, v := range m.encoders {
		if strings.Contains(strings.ToLower(s), strings.ToLower(k)) {
			// match
			return v, true
		}
	}
	return nil, false
}

type jsonEncoder struct{}

var _ Encoder = (*jsonEncoder)(nil)

// JSON is an Encoder for JSON.
var JSON *jsonEncoder

func (j *jsonEncoder) Encode(w http.ResponseWriter, r *http.Request, v interface{}) error {
	return json.NewEncoder(w).Encode(v)
}

func (j *jsonEncoder) ContentType(w http.ResponseWriter, r *http.Request) string {
	return "application/json"
}
<commit_msg>small tidy up<commit_after>package respond

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

	"sync"
)

// Encoder descirbes an object capable of encoding
// a response.
type Encoder interface {
	Encode(w http.ResponseWriter, r *http.Request, v interface{}) error
	ContentType(w http.ResponseWriter, r *http.Request) string
}

// DefaultEncoder is the Encoder to use if no other can
// be matched.
var DefaultEncoder = JSON

var encoders = &encodersMap{
	encoders: map[string]Encoder{"json": JSON},
}

// Encoders allows the adding, removing and matching of encoders
// by fuzzy strings, usually the Accept request header.
// By default, JSON is added.
func Encoders() interface {
	Add(match string, encoder Encoder)
	Match(s string) (Encoder, bool)
	Del(e Encoder)
} {
	return encoders
}

type encodersMap struct {
	lock     sync.RWMutex
	encoders map[string]Encoder
}

func (m *encodersMap) Add(match string, e Encoder) {
	m.lock.Lock()
	m.encoders[match] = e
	m.lock.Unlock()
}

func (m *encodersMap) Del(e Encoder) {
	m.lock.Lock()
	defer m.lock.Unlock()
	found := false
	for k, v := range m.encoders {
		if v == e {
			delete(m.encoders, k)
			found = true
		}
	}
	if !found {
		panic("respond: encoder not found")
	}
}

func (m *encodersMap) Match(s string) (Encoder, bool) {
	m.lock.RLock()
	defer m.lock.RUnlock()
	for k, v := range m.encoders {
		if strings.Contains(strings.ToLower(s), strings.ToLower(k)) {
			// match
			return v, true
		}
	}
	return nil, false
}

type jsonEncoder struct{}

var _ Encoder = (*jsonEncoder)(nil)

// JSON is an Encoder for JSON.
var JSON *jsonEncoder

func (*jsonEncoder) Encode(w http.ResponseWriter, r *http.Request, v interface{}) error {
	return json.NewEncoder(w).Encode(v)
}

func (*jsonEncoder) ContentType(w http.ResponseWriter, r *http.Request) string {
	return "application/json"
}
<|endoftext|>


<commit_before>package env_test

import (
	"os"
	"testing"
	. "github.com/themccallister/env"
)

func TestCanGetTheEnvAppModeDefault(t *testing.T) {
	e := Set{}

	if e.AppMode() != "development" {
		t.Fatalf("expected the environment to be development, got %v instead", e.AppMode)
	}
}

func TestCanDefineTheAppModeUsingEnvVars(t *testing.T) {
	e := Set{}
	os.Setenv("APP_MODE", "production")
	if e.AppMode() != "production" {
		t.Fatalf("expected the environment to be production, got %v instead", e.AppMode)
	}
	if e.AppMode() == "development" {
		t.Fatalf("expected the environment to be production, got %v instead", e.AppMode)
	}
}

func TestGetOrWillReturnTheRequestedEnvVarOrTheDefaultThatWasPassed(t *testing.T) {
	e := Set{}
	notSet := e.GetOr("APP_FAKE_VAR", "https://mccallister.io")
	if notSet != "https://mccallister.io" {
		t.Fatalf("expected the environment var to be development, got %v instead", e.AppMode)
	}

	os.Setenv("APP_FAKE_VAR", "1234")
	set := e.GetOr("APP_FAKE_VAR", "https://www.google.com")
	if set != "1234" {
		t.Fatalf("expected the environment var to be 1234, got %v instead", e.AppMode)
	}
	if set == "https://www.google.com" {
		t.Fatalf("expected the environment var to be 1234, got %v instead", e.AppMode)
	}
}

func TestEnvCanSetADefaultModeToOverrideThePackageDefaultAppMode(t *testing.T) {
	// clear the env to be sure
	os.Unsetenv("APP_MODE")
	e := Set{DefaultMode: "staging"}
	m := e.AppMode()
	if m != "staging" {
		t.Fatalf("expected the app mode to be staging, got %v instead", m)
	}
}

func TestCanGetTheAppKeyFromEnv(t *testing.T) {
	os.Setenv("APP_KEY", "1234")
	e := Set{}
	k, err := e.AppKey()
	if err != nil {
		t.Fatalf("could not get the AppKey from the Env, got %v instead", err)
	}
	if k != "1234" {
		t.Fatalf("expected the key to not be empty, got %v instead", k)
	}
}

func TestGettingTheAppKeyWithoutTheEnvVarReturnsAnError(t *testing.T) {
	os.Unsetenv("APP_KEY")
	e := Set{}
	_, err := e.AppKey()
	if err == nil {
		t.Fatal("expected an error, but none was returned")
	}
}

func TestDefaultAppKeyCanBeSetAndReturnedIfTheAppKeyIsNotSetInEnv(t *testing.T) {
	os.Unsetenv("APP_KEY")
	e := Set{DefaultKey: "654321"}
	k, err := e.AppKey()
	if err != nil {
		t.Fatalf("an unexpected error occured, %v", err)
	}
	if k != "654321" {
		t.Fatalf("expected the app key to be 654321, got %v instead", k)
	}
}
<commit_msg>nothing significant, saw some nasty spaces that needed zapped<commit_after>package env_test

import (
	. "github.com/themccallister/env"
	"os"
	"testing"
)

func TestCanGetTheEnvAppModeDefault(t *testing.T) {
	e := Set{}
	if e.AppMode() != "development" {
		t.Fatalf("expected the environment to be development, got %v instead", e.AppMode)
	}
}

func TestCanDefineTheAppModeUsingEnvVars(t *testing.T) {
	e := Set{}
	os.Setenv("APP_MODE", "production")
	if e.AppMode() != "production" {
		t.Fatalf("expected the environment to be production, got %v instead", e.AppMode)
	}
	if e.AppMode() == "development" {
		t.Fatalf("expected the environment to be production, got %v instead", e.AppMode)
	}
}

func TestGetOrWillReturnTheRequestedEnvVarOrTheDefaultThatWasPassed(t *testing.T) {
	e := Set{}
	notSet := e.GetOr("APP_FAKE_VAR", "https://mccallister.io")
	if notSet != "https://mccallister.io" {
		t.Fatalf("expected the environment var to be development, got %v instead", e.AppMode)
	}
	os.Setenv("APP_FAKE_VAR", "1234")
	set := e.GetOr("APP_FAKE_VAR", "https://www.google.com")
	if set != "1234" {
		t.Fatalf("expected the environment var to be 1234, got %v instead", e.AppMode)
	}
	if set == "https://www.google.com" {
		t.Fatalf("expected the environment var to be 1234, got %v instead", e.AppMode)
	}
}

func TestEnvCanSetADefaultModeToOverrideThePackageDefaultAppMode(t *testing.T) {
	// clear the env to be sure
	os.Unsetenv("APP_MODE")
	e := Set{DefaultMode: "staging"}
	m := e.AppMode()
	if m != "staging" {
		t.Fatalf("expected the app mode to be staging, got %v instead", m)
	}
}

func TestCanGetTheAppKeyFromEnv(t *testing.T) {
	os.Setenv("APP_KEY", "1234")
	e := Set{}
	k, err := e.AppKey()
	if err != nil {
		t.Fatalf("could not get the AppKey from the Env, got %v instead", err)
	}
	if k != "1234" {
		t.Fatalf("expected the key to not be empty, got %v instead", k)
	}
}

func TestGettingTheAppKeyWithoutTheEnvVarReturnsAnError(t *testing.T) {
	os.Unsetenv("APP_KEY")
	e := Set{}
	_, err := e.AppKey()
	if err == nil {
		t.Fatal("expected an error, but none was returned")
	}
}

func TestDefaultAppKeyCanBeSetAndReturnedIfTheAppKeyIsNotSetInEnv(t *testing.T) {
	os.Unsetenv("APP_KEY")
	e := Set{DefaultKey: "654321"}
	k, err := e.AppKey()
	if err != nil {
		t.Fatalf("an unexpected error occured, %v", err)
	}
	if k != "654321" {
		t.Fatalf("expected the app key to be 654321, got %v instead", k)
	}
}
<|endoftext|>


<commit_before>package restruct

import (
	"encoding/binary"
	"fmt"
	"reflect"
)

// Field represents a structure field, similar to reflect.StructField.
type Field struct {
	Name    string
	CanSet  bool
	Type    reflect.Type
	DefType reflect.Type
	Order   binary.ByteOrder
}

// FieldsFromStruct returns a slice of fields for binary packing and unpacking.
func FieldsFromStruct(typ reflect.Type) (result []Field) {
	if typ.Kind() != reflect.Struct {
		panic(fmt.Errorf("tried to get fields from non-struct type %s", typ.Kind().String()))
	}

	count := typ.NumField()
	for i := 0; i < count; i++ {
		val := typ.Field(i)

		// Parse struct tag
		opts := MustParseTag(val.Tag.Get("struct"))
		if opts.Ignore {
			continue
		}

		// Derive type
		ftyp := val.Type
		if opts.Type != nil {
			ftyp = opts.Type
		}

		result = append(result, Field{
			Name:    val.Name,
			CanSet:  true,
			Type:    ftyp,
			DefType: val.Type,
			Order:   opts.Order,
		})
	}

	return result
}

// IsTypeTrivial determines if a given type is constant-size.
func IsTypeTrivial(typ reflect.Type) bool {
	switch typ.Kind() {
	case reflect.Bool,
		reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64,
		reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64,
		reflect.Uintptr,
		reflect.Float32,
		reflect.Float64,
		reflect.Complex64,
		reflect.Complex128:
		return true
	case reflect.Array, reflect.Ptr, reflect.Slice:
		return IsTypeTrivial(typ.Elem())
	case reflect.Struct:
		for _, field := range FieldsFromStruct(typ) {
			if !IsTypeTrivial(field.Type) {
				return false
			}
		}
		return true
	default:
		return true
	}
}

// Elem synthesizes a field from an element of a slice, array, or pointer.
// If Field is not a slice, array, or pointer, FieldFromArray panics.
func (f *Field) Elem() *Field {
	return &Field{
		Name:    "*" + f.Name,
		CanSet:  f.CanSet,
		Type:    f.Type.Elem(),
		DefType: f.Type.Elem(),
		Order:   f.Order,
	}
}

// SizeOf determines what the binary size of the field should be.
func (f *Field) SizeOf(val reflect.Value) {

}
<commit_msg>IsTypeTrivial: Slice is not trivial<commit_after>package restruct

import (
	"encoding/binary"
	"fmt"
	"reflect"
)

// Field represents a structure field, similar to reflect.StructField.
type Field struct {
	Name    string
	CanSet  bool
	Type    reflect.Type
	DefType reflect.Type
	Order   binary.ByteOrder
}

// FieldsFromStruct returns a slice of fields for binary packing and unpacking.
func FieldsFromStruct(typ reflect.Type) (result []Field) {
	if typ.Kind() != reflect.Struct {
		panic(fmt.Errorf("tried to get fields from non-struct type %s", typ.Kind().String()))
	}

	count := typ.NumField()
	for i := 0; i < count; i++ {
		val := typ.Field(i)

		// Parse struct tag
		opts := MustParseTag(val.Tag.Get("struct"))
		if opts.Ignore {
			continue
		}

		// Derive type
		ftyp := val.Type
		if opts.Type != nil {
			ftyp = opts.Type
		}

		result = append(result, Field{
			Name:    val.Name,
			CanSet:  true,
			Type:    ftyp,
			DefType: val.Type,
			Order:   opts.Order,
		})
	}

	return result
}

// IsTypeTrivial determines if a given type is constant-size.
func IsTypeTrivial(typ reflect.Type) bool {
	switch typ.Kind() {
	case reflect.Bool,
		reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64,
		reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64,
		reflect.Uintptr,
		reflect.Float32,
		reflect.Float64,
		reflect.Complex64,
		reflect.Complex128:
		return true
	case reflect.Array, reflect.Ptr:
		return IsTypeTrivial(typ.Elem())
	case reflect.Struct:
		for _, field := range FieldsFromStruct(typ) {
			if !IsTypeTrivial(field.Type) {
				return false
			}
		}
		return true
	default:
		return true
	}
}

// Elem synthesizes a field from an element of a slice, array, or pointer.
// If Field is not a slice, array, or pointer, FieldFromArray panics.
func (f *Field) Elem() *Field {
	return &Field{
		Name:    "*" + f.Name,
		CanSet:  f.CanSet,
		Type:    f.Type.Elem(),
		DefType: f.Type.Elem(),
		Order:   f.Order,
	}
}

// SizeOf determines what the binary size of the field should be.
func (f *Field) SizeOf(val reflect.Value) {

}
<|endoftext|>


<commit_before>package eth

import (
	"container/list"
	"github.com/ethereum/ethchain-go"
	"github.com/ethereum/ethdb-go"
	"github.com/ethereum/ethutil-go"
	"github.com/ethereum/ethwire-go"
	"log"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
	// Loop thru the peers and close them (if we had them)
	for e := peers.Front(); e != nil; e = e.Next() {
		if peer, ok := e.Value.(*Peer); ok {
			callback(peer, e)
		}
	}
}

const (
	processReapingTimeout = 60 // TODO increase
)

type Ethereum struct {
	// Channel for shutting down the ethereum
	shutdownChan chan bool
	quit         chan bool
	// DB interface
	//db *ethdb.LDBDatabase
	db ethutil.Database
	// Block manager for processing new blocks and managing the block chain
	BlockManager *ethchain.BlockManager
	// The transaction pool. Transaction can be pushed on this pool
	// for later including in the blocks
	TxPool *ethchain.TxPool
	// Peers (NYI)
	peers *list.List
	// Nonce
	Nonce uint64

	Addr net.Addr

	peerMut sync.Mutex

	// Capabilities for outgoing peers
	serverCaps Caps

	nat NAT

	// Specifies the desired amount of maximum peers
	MaxPeers int
}

func New(caps Caps, usePnp bool) (*Ethereum, error) {
	//db, err := ethdb.NewLDBDatabase()
	db, err := ethdb.NewMemDatabase()
	if err != nil {
		return nil, err
	}

	var nat NAT
	if usePnp {
		nat, err = Discover()
		if err != nil {
			log.Println("UPnP failed", err)
		}
	}

	ethutil.Config.Db = db

	nonce, _ := ethutil.RandomUint64()
	ethereum := &Ethereum{
		shutdownChan: make(chan bool),
		quit:         make(chan bool),
		db:           db,
		peers:        list.New(),
		Nonce:        nonce,
		serverCaps:   caps,
		nat:          nat,
		MaxPeers:     5,
	}
	ethereum.TxPool = ethchain.NewTxPool()
	ethereum.TxPool.Speaker = ethereum
	ethereum.BlockManager = ethchain.NewBlockManager(ethereum)

	ethereum.TxPool.BlockManager = ethereum.BlockManager
	ethereum.BlockManager.TransactionPool = ethereum.TxPool

	return ethereum, nil
}

func (s *Ethereum) AddPeer(conn net.Conn) {
	peer := NewPeer(conn, s, true)

	if peer != nil && s.peers.Len() < s.MaxPeers {
		s.peers.PushBack(peer)
		peer.Start()
	}
}

func (s *Ethereum) ProcessPeerList(addrs []string) {
	for _, addr := range addrs {
		// TODO Probably requires some sanity checks
		s.ConnectToPeer(addr)
	}
}

func (s *Ethereum) ConnectToPeer(addr string) error {
	var alreadyConnected bool

	eachPeer(s.peers, func(p *Peer, v *list.Element) {
		if p.conn == nil {
			return
		}
		phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
		ahost, _, _ := net.SplitHostPort(addr)

		if phost == ahost {
			alreadyConnected = true
			return
		}
	})

	if alreadyConnected {
		return nil
	}

	peer := NewOutboundPeer(addr, s, s.serverCaps)

	s.peers.PushBack(peer)

	return nil
}

func (s *Ethereum) OutboundPeers() []*Peer {
	// Create a new peer slice with at least the length of the total peers
	outboundPeers := make([]*Peer, s.peers.Len())
	length := 0
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		if !p.inbound && p.conn != nil {
			outboundPeers[length] = p
			length++
		}
	})

	return outboundPeers[:length]
}

func (s *Ethereum) InboundPeers() []*Peer {
	// Create a new peer slice with at least the length of the total peers
	inboundPeers := make([]*Peer, s.peers.Len())
	length := 0
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		if p.inbound {
			inboundPeers[length] = p
			length++
		}
	})

	return inboundPeers[:length]
}

func (s *Ethereum) InOutPeers() []*Peer {
	// Reap the dead peers first
	s.reapPeers()

	// Create a new peer slice with at least the length of the total peers
	inboundPeers := make([]*Peer, s.peers.Len())
	length := 0
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		// Only return peers with an actual ip
		if len(p.host) > 0 {
			inboundPeers[length] = p
			length++
		}
	})

	return inboundPeers[:length]
}

func (s *Ethereum) Broadcast(msgType ethwire.MsgType, data []interface{}) {
	msg := ethwire.NewMessage(msgType, data)
	s.BroadcastMsg(msg)
}

func (s *Ethereum) BroadcastMsg(msg *ethwire.Msg) {
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		p.QueueMessage(msg)
	})
}

func (s *Ethereum) Peers() *list.List {
	return s.peers
}

func (s *Ethereum) reapPeers() {
	s.peerMut.Lock()
	defer s.peerMut.Unlock()

	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
			s.peers.Remove(e)
		}
	})
}

func (s *Ethereum) ReapDeadPeerHandler() {
	reapTimer := time.NewTicker(processReapingTimeout * time.Second)

	for {
		select {
		case <-reapTimer.C:
			s.reapPeers()
		}
	}
}

// Start the ethereum
func (s *Ethereum) Start() {
	// Bind to addr and port
	ln, err := net.Listen("tcp", ":30303")
	if err != nil {
		log.Println("Connection listening disabled. Acting as client")
	} else {
		// Starting accepting connections
		log.Println("Ready and accepting connections")
		// Start the peer handler
		go s.peerHandler(ln)
	}

	if s.nat != nil {
		go s.upnpUpdateThread()
	}

	// Start the reaping processes
	go s.ReapDeadPeerHandler()

	// Start the tx pool
	s.TxPool.Start()
}

func (s *Ethereum) peerHandler(listener net.Listener) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println(err)

			continue
		}

		go s.AddPeer(conn)
	}
}

func (s *Ethereum) Stop() {
	// Close the database
	defer s.db.Close()

	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		p.Stop()
	})

	close(s.quit)

	s.TxPool.Stop()
	s.BlockManager.Stop()

	s.shutdownChan <- true
}

// This function will wait for a shutdown and resumes main thread execution
func (s *Ethereum) WaitForShutdown() {
	<-s.shutdownChan
}

func (s *Ethereum) upnpUpdateThread() {
	// Go off immediately to prevent code duplication, thereafter we renew
	// lease every 15 minutes.
	timer := time.NewTimer(0 * time.Second)
	lport, _ := strconv.ParseInt("30303", 10, 16)
	first := true
out:
	for {
		select {
		case <-timer.C:
			var err error
			_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
			if err != nil {
				log.Println("can't add UPnP port mapping:", err)
				break out
			}
			if first && err == nil {
				_, err = s.nat.GetExternalAddress()
				if err != nil {
					log.Println("UPnP can't get external address:", err)
					continue out
				}
				first = false
			}
			timer.Reset(time.Minute * 15)
		case <-s.quit:
			break out
		}
	}

	timer.Stop()

	if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
		log.Println("unable to remove UPnP port mapping:", err)
	} else {
		log.Println("succesfully disestablished UPnP port mapping")
	}
}
<commit_msg>level db back in<commit_after>package eth

import (
	"container/list"
	"github.com/ethereum/ethchain-go"
	"github.com/ethereum/ethdb-go"
	"github.com/ethereum/ethutil-go"
	"github.com/ethereum/ethwire-go"
	"log"
	"net"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
	// Loop thru the peers and close them (if we had them)
	for e := peers.Front(); e != nil; e = e.Next() {
		if peer, ok := e.Value.(*Peer); ok {
			callback(peer, e)
		}
	}
}

const (
	processReapingTimeout = 60 // TODO increase
)

type Ethereum struct {
	// Channel for shutting down the ethereum
	shutdownChan chan bool
	quit         chan bool
	// DB interface
	//db *ethdb.LDBDatabase
	db ethutil.Database
	// Block manager for processing new blocks and managing the block chain
	BlockManager *ethchain.BlockManager
	// The transaction pool. Transaction can be pushed on this pool
	// for later including in the blocks
	TxPool *ethchain.TxPool
	// Peers (NYI)
	peers *list.List
	// Nonce
	Nonce uint64

	Addr net.Addr

	peerMut sync.Mutex

	// Capabilities for outgoing peers
	serverCaps Caps

	nat NAT

	// Specifies the desired amount of maximum peers
	MaxPeers int
}

func New(caps Caps, usePnp bool) (*Ethereum, error) {
	db, err := ethdb.NewLDBDatabase()
	//db, err := ethdb.NewMemDatabase()
	if err != nil {
		return nil, err
	}

	var nat NAT
	if usePnp {
		nat, err = Discover()
		if err != nil {
			log.Println("UPnP failed", err)
		}
	}

	ethutil.Config.Db = db

	nonce, _ := ethutil.RandomUint64()
	ethereum := &Ethereum{
		shutdownChan: make(chan bool),
		quit:         make(chan bool),
		db:           db,
		peers:        list.New(),
		Nonce:        nonce,
		serverCaps:   caps,
		nat:          nat,
		MaxPeers:     5,
	}
	ethereum.TxPool = ethchain.NewTxPool()
	ethereum.TxPool.Speaker = ethereum
	ethereum.BlockManager = ethchain.NewBlockManager(ethereum)

	ethereum.TxPool.BlockManager = ethereum.BlockManager
	ethereum.BlockManager.TransactionPool = ethereum.TxPool

	return ethereum, nil
}

func (s *Ethereum) AddPeer(conn net.Conn) {
	peer := NewPeer(conn, s, true)

	if peer != nil && s.peers.Len() < s.MaxPeers {
		s.peers.PushBack(peer)
		peer.Start()
	}
}

func (s *Ethereum) ProcessPeerList(addrs []string) {
	for _, addr := range addrs {
		// TODO Probably requires some sanity checks
		s.ConnectToPeer(addr)
	}
}

func (s *Ethereum) ConnectToPeer(addr string) error {
	var alreadyConnected bool

	eachPeer(s.peers, func(p *Peer, v *list.Element) {
		if p.conn == nil {
			return
		}
		phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
		ahost, _, _ := net.SplitHostPort(addr)

		if phost == ahost {
			alreadyConnected = true
			return
		}
	})

	if alreadyConnected {
		return nil
	}

	peer := NewOutboundPeer(addr, s, s.serverCaps)

	s.peers.PushBack(peer)

	return nil
}

func (s *Ethereum) OutboundPeers() []*Peer {
	// Create a new peer slice with at least the length of the total peers
	outboundPeers := make([]*Peer, s.peers.Len())
	length := 0
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		if !p.inbound && p.conn != nil {
			outboundPeers[length] = p
			length++
		}
	})

	return outboundPeers[:length]
}

func (s *Ethereum) InboundPeers() []*Peer {
	// Create a new peer slice with at least the length of the total peers
	inboundPeers := make([]*Peer, s.peers.Len())
	length := 0
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		if p.inbound {
			inboundPeers[length] = p
			length++
		}
	})

	return inboundPeers[:length]
}

func (s *Ethereum) InOutPeers() []*Peer {
	// Reap the dead peers first
	s.reapPeers()

	// Create a new peer slice with at least the length of the total peers
	inboundPeers := make([]*Peer, s.peers.Len())
	length := 0
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		// Only return peers with an actual ip
		if len(p.host) > 0 {
			inboundPeers[length] = p
			length++
		}
	})

	return inboundPeers[:length]
}

func (s *Ethereum) Broadcast(msgType ethwire.MsgType, data []interface{}) {
	msg := ethwire.NewMessage(msgType, data)
	s.BroadcastMsg(msg)
}

func (s *Ethereum) BroadcastMsg(msg *ethwire.Msg) {
	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		p.QueueMessage(msg)
	})
}

func (s *Ethereum) Peers() *list.List {
	return s.peers
}

func (s *Ethereum) reapPeers() {
	s.peerMut.Lock()
	defer s.peerMut.Unlock()

	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		if atomic.LoadInt32(&p.disconnect) == 1 || (p.inbound && (time.Now().Unix()-p.lastPong) > int64(5*time.Minute)) {
			s.peers.Remove(e)
		}
	})
}

func (s *Ethereum) ReapDeadPeerHandler() {
	reapTimer := time.NewTicker(processReapingTimeout * time.Second)

	for {
		select {
		case <-reapTimer.C:
			s.reapPeers()
		}
	}
}

// Start the ethereum
func (s *Ethereum) Start() {
	// Bind to addr and port
	ln, err := net.Listen("tcp", ":30303")
	if err != nil {
		log.Println("Connection listening disabled. Acting as client")
	} else {
		// Starting accepting connections
		log.Println("Ready and accepting connections")
		// Start the peer handler
		go s.peerHandler(ln)
	}

	if s.nat != nil {
		go s.upnpUpdateThread()
	}

	// Start the reaping processes
	go s.ReapDeadPeerHandler()

	// Start the tx pool
	s.TxPool.Start()
}

func (s *Ethereum) peerHandler(listener net.Listener) {
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println(err)

			continue
		}

		go s.AddPeer(conn)
	}
}

func (s *Ethereum) Stop() {
	// Close the database
	defer s.db.Close()

	eachPeer(s.peers, func(p *Peer, e *list.Element) {
		p.Stop()
	})

	close(s.quit)

	s.TxPool.Stop()
	s.BlockManager.Stop()

	s.shutdownChan <- true
}

// This function will wait for a shutdown and resumes main thread execution
func (s *Ethereum) WaitForShutdown() {
	<-s.shutdownChan
}

func (s *Ethereum) upnpUpdateThread() {
	// Go off immediately to prevent code duplication, thereafter we renew
	// lease every 15 minutes.
	timer := time.NewTimer(0 * time.Second)
	lport, _ := strconv.ParseInt("30303", 10, 16)
	first := true
out:
	for {
		select {
		case <-timer.C:
			var err error
			_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
			if err != nil {
				log.Println("can't add UPnP port mapping:", err)
				break out
			}
			if first && err == nil {
				_, err = s.nat.GetExternalAddress()
				if err != nil {
					log.Println("UPnP can't get external address:", err)
					continue out
				}
				first = false
			}
			timer.Reset(time.Minute * 15)
		case <-s.quit:
			break out
		}
	}

	timer.Stop()

	if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
		log.Println("unable to remove UPnP port mapping:", err)
	} else {
		log.Println("succesfully disestablished UPnP port mapping")
	}
}
<|endoftext|>


<commit_before>package httpdir

import (
	"bytes"
	"os"
	"strings"
	"time"
)

type fileBytes struct {
	data    []byte
	modTime time.Time
}

// FileBytes provides an implementation of Node that takes a byte slice as its
// data source
func FileBytes(data []byte, modTime time.Time) Node {
	return fileBytes{
		data,
		modTime,
	}
}

func (f fileBytes) Size() int64 {
	return int64(len(f.data))
}

func (fileBytes) Mode() os.FileMode {
	return ModeFile
}

func (f fileBytes) ModTime() time.Time {
	return f.modTime
}

func (f fileBytes) Open() (File, error) {
	return fileBytesOpen{bytes.NewReader(f.data)}, nil
}

type fileBytesOpen struct {
	*bytes.Reader
}

func (fileBytesOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

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

type fileString struct {
	data    string
	modTime time.Time
}

// FileString provides an implementation of Node that takes a string as its
// data source
func FileString(data string, modTime time.Time) Node {
	return fileString{
		data,
		modTime,
	}
}

func (f fileString) Size() int64 {
	return int64(len(f.data))
}

func (fileString) Mode() os.FileMode {
	return ModeFile
}

func (f fileString) ModTime() time.Time {
	return f.modTime
}

func (f fileString) Open() (File, error) {
	return fileStringOpen{strings.NewReader(f.data)}, nil
}

type fileStringOpen struct {
	*strings.Reader
}

func (fileStringOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

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

// OSFile is the path of a file in the real filesystem to be put into the
// im-memory filesystem
type OSFile string

// Size returns the size of the file
func (o OSFile) Size() int64 {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Size()
}

// Mode returns the Mode of the file
func (o OSFile) Mode() os.FileMode {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Mode()
}

// ModTime returns the ModTime of the file
func (o OSFile) ModTime() time.Time {
	s, err := os.Stat(string(o))
	if err != nil {
		return time.Time{}
	}
	return s.ModTime()
}

// Open opens the file, returning it as a File
func (o OSFile) Open() (File, error) {
	return os.Open(string(o))
}
<commit_msg>Added Decompress func to decompress a gzipped node into a new node<commit_after>package httpdir

import (
	"bytes"
	"compress/gzip"
	"os"
	"strings"
	"time"
)

type fileBytes struct {
	data    []byte
	modTime time.Time
}

// FileBytes provides an implementation of Node that takes a byte slice as its
// data source
func FileBytes(data []byte, modTime time.Time) Node {
	return fileBytes{
		data,
		modTime,
	}
}

func (f fileBytes) Size() int64 {
	return int64(len(f.data))
}

func (fileBytes) Mode() os.FileMode {
	return ModeFile
}

func (f fileBytes) ModTime() time.Time {
	return f.modTime
}

func (f fileBytes) Open() (File, error) {
	return fileBytesOpen{bytes.NewReader(f.data)}, nil
}

type fileBytesOpen struct {
	*bytes.Reader
}

func (fileBytesOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

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

type fileString struct {
	data    string
	modTime time.Time
}

// FileString provides an implementation of Node that takes a string as its
// data source
func FileString(data string, modTime time.Time) Node {
	return fileString{
		data,
		modTime,
	}
}

func (f fileString) Size() int64 {
	return int64(len(f.data))
}

func (fileString) Mode() os.FileMode {
	return ModeFile
}

func (f fileString) ModTime() time.Time {
	return f.modTime
}

func (f fileString) Open() (File, error) {
	return fileStringOpen{strings.NewReader(f.data)}, nil
}

type fileStringOpen struct {
	*strings.Reader
}

func (fileStringOpen) Readdir(int) ([]os.FileInfo, error) {
	return nil, os.ErrInvalid
}

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

// OSFile is the path of a file in the real filesystem to be put into the
// im-memory filesystem
type OSFile string

// Size returns the size of the file
func (o OSFile) Size() int64 {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Size()
}

// Mode returns the Mode of the file
func (o OSFile) Mode() os.FileMode {
	s, err := os.Stat(string(o))
	if err != nil {
		return 0
	}
	return s.Mode()
}

// ModTime returns the ModTime of the file
func (o OSFile) ModTime() time.Time {
	s, err := os.Stat(string(o))
	if err != nil {
		return time.Time{}
	}
	return s.ModTime()
}

// Open opens the file, returning it as a File
func (o OSFile) Open() (File, error) {
	return os.Open(string(o))
}

// Decompressed creates a new node that contains the decompressed gzip data
// from the given node
func Decompressed(node Node, size int) (Node, error) {
	f, err := node.Open()
	if err != nil {
		return nil, err
	}
	defer f.Close()
	g, err := gzip.NewReader(f)
	if err != nil {
		return nil, err
	}
	defer g.Close()
	buf := make([]byte, size)

	var read int
	for read < size {
		n, err := g.Read(buf[read:])
		if err != nil {
			return nil, err
		}
		read += n
	}

	return FileBytes(buf, node.ModTime()), nil
}
<|endoftext|>


<commit_before>package dropbox

import (
	"encoding/json"
	"io"
	"time"
)

// Files client for files and folders.
type Files struct {
	*Client
}

// NewFiles client.
func NewFiles(config *Config) *Files {
	return &Files{
		Client: &Client{
			Config: config,
		},
	}
}

// WriteMode determines what to do if the file already exists.
type WriteMode string

// Supported write modes.
const (
	WriteModeAdd       WriteMode = "add"
	WriteModeOverwrite           = "overwrite"
)

// Metadata for a file or folder.
type Metadata struct {
	Tag            string    `json:".tag"`
	Name           string    `json:"name"`
	PathLower      string    `json:"path_lower"`
	ClientModified time.Time `json:"client_modified"`
	ServerModified time.Time `json:"server_modified"`
	Rev            string    `json:"rev"`
	Size           uint64    `json:"size"`
	ID             string    `json:"id"`
}

// GetMetadataInput request input.
type GetMetadataInput struct {
	Path             string `json:"path"`
	IncludeMediaInfo bool   `json:"include_media_info"`
}

// GetMetadataOutput request output.
type GetMetadataOutput struct {
	Metadata
}

// GetMetadata returns the metadata for a file or folder.
func (c *Files) GetMetadata(in *GetMetadataInput) (out *GetMetadataOutput, err error) {
	body, err := c.call("/files/get_metadata", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// CreateFolderInput request input.
type CreateFolderInput struct {
	Path string `json:"path"`
}

// CreateFolderOutput request output.
type CreateFolderOutput struct {
	Name      string `json:"name"`
	PathLower string `json:"path_lower"`
	ID        string `json:"id"`
}

// CreateFolder creates a folder.
func (c *Files) CreateFolder(in *CreateFolderInput) (out *CreateFolderOutput, err error) {
	body, err := c.call("/files/create_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DeleteInput request input.
type DeleteInput struct {
	Path string `json:"path"`
}

// DeleteOutput request output.
type DeleteOutput struct {
	Metadata
}

// Delete a file or folder and its contents.
func (c *Files) Delete(in *DeleteInput) (out *DeleteOutput, err error) {
	body, err := c.call("/files/delete", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// CopyInput request input.
type CopyInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// CopyOutput request output.
type CopyOutput struct {
	Metadata
}

// Copy a file or folder to a different location.
func (c *Files) Copy(in *CopyInput) (out *CopyOutput, err error) {
	body, err := c.call("/files/copy", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// MoveInput request input.
type MoveInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// MoveOutput request output.
type MoveOutput struct {
	Metadata
}

// Move a file or folder to a different location.
func (c *Files) Move(in *MoveInput) (out *MoveOutput, err error) {
	body, err := c.call("/files/move", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// RestoreInput request input.
type RestoreInput struct {
	Path string `json:"path"`
	Rev  string `json:"rev"`
}

// RestoreOutput request output.
type RestoreOutput struct {
	Metadata
}

// Restore a file to a specific revision.
func (c *Files) Restore(in *RestoreInput) (out *RestoreOutput, err error) {
	body, err := c.call("/files/restore", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// ListFolderInput request input.
type ListFolderInput struct {
	Path             string `json:"path"`
	Recursive        bool   `json:"recursive"`
	IncludeMediaInfo bool   `json:"include_media_info"`
	IncludeDeleted   bool   `json:"include_deleted"`
}

// ListFolderOutput request output.
type ListFolderOutput struct {
	Cursor  string `json:"cursor"`
	HasMore bool   `json:"has_more"`
	Entries []*Metadata
}

// ListFolder returns the metadata for a file or folder.
func (c *Files) ListFolder(in *ListFolderInput) (out *ListFolderOutput, err error) {
	body, err := c.call("/files/list_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// SearchMode determines how a search is performed.
type SearchMode string

// Supported search modes.
const (
	SearchModeFilename           SearchMode = "filename"
	SearchModeFilenameAndContent            = "filename_and_content"
	SearchModeDeletedFilename               = "deleted_filename"
)

// SearchMatch represents the type of match made.
type SearchMatchType string

// Supported search match types.
const (
	SearchMatchFilename SearchMatchType = "filename"
	SearchMatchContent                  = "content"
	SearchMatchBoth                     = "both"
)

// SearchMatch represents a matched file or folder.
type SearchMatch struct {
	MatchType struct {
		Tag SearchMatchType `json:".tag"`
	} `json:"match_type"`
	Metadata *Metadata `json:"metadata"`
}

// SearchInput request input.
type SearchInput struct {
	Path       string     `json:"path"`
	Query      string     `json:"query"`
	Start      uint64     `json:"start,omitempty"`
	MaxResults uint64     `json:"max_results,omitempty"`
	Mode       SearchMode `json:"mode"`
}

// SearchOutput request output.
type SearchOutput struct {
	Matches []*SearchMatch `json:"matches"`
	More    bool           `json:"more"`
	Start   uint64         `json:"start"`
}

// Search for files and folders.
func (c *Files) Search(in *SearchInput) (out *SearchOutput, err error) {
	if in.Mode == "" {
		in.Mode = SearchModeFilename
	}

	body, err := c.call("/files/search", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// UploadInput request input.
type UploadInput struct {
	Path           string    `json:"path"`
	Mode           WriteMode `json:"mode"`
	AutoRename     bool      `json:"autorename"`
	Mute           bool      `json:"mute"`
	ClientModified time.Time `json:"client_modified,omitempty"`
	Reader         io.Reader `json:"-"`
}

// UploadOutput request output.
type UploadOutput struct {
	Metadata
}

// Upload a file smaller than 150MB.
func (c *Files) Upload(in *UploadInput) (out *UploadOutput, err error) {
	body, err := c.download("/files/upload", in, in.Reader)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DownloadInput request input.
type DownloadInput struct {
	Path string `json:"path"`
}

// DownloadOutput request output.
type DownloadOutput struct {
	Body io.ReadCloser
}

// Download a file.
func (c *Files) Download(in *DownloadInput) (out *DownloadOutput, err error) {
	body, err := c.download("/files/download", in, nil)
	if err != nil {
		return
	}

	out = &DownloadOutput{body}
	return
}

// GetPreviewInput request input.
type GetPreviewInput struct {
	Path string `json:"path"`
}

// GetPreviewOutput request output.
type GetPreviewOutput struct {
	Body io.ReadCloser
}

// GetPreview a preview for a file. Currently previews are only generated for the
// files with the following extensions: .doc, .docx, .docm, .ppt, .pps, .ppsx,
// .ppsm, .pptx, .pptm, .xls, .xlsx, .xlsm, .rtf
func (c *Files) GetPreview(in *GetPreviewInput) (out *GetPreviewOutput, err error) {
	body, err := c.download("/files/get_preview", in, nil)
	if err != nil {
		return
	}

	out = &GetPreviewOutput{body}
	return
}
<commit_msg>add Files.PermanentlyDelete()<commit_after>package dropbox

import (
	"encoding/json"
	"io"
	"time"
)

// Files client for files and folders.
type Files struct {
	*Client
}

// NewFiles client.
func NewFiles(config *Config) *Files {
	return &Files{
		Client: &Client{
			Config: config,
		},
	}
}

// WriteMode determines what to do if the file already exists.
type WriteMode string

// Supported write modes.
const (
	WriteModeAdd       WriteMode = "add"
	WriteModeOverwrite           = "overwrite"
)

// Metadata for a file or folder.
type Metadata struct {
	Tag            string    `json:".tag"`
	Name           string    `json:"name"`
	PathLower      string    `json:"path_lower"`
	ClientModified time.Time `json:"client_modified"`
	ServerModified time.Time `json:"server_modified"`
	Rev            string    `json:"rev"`
	Size           uint64    `json:"size"`
	ID             string    `json:"id"`
}

// GetMetadataInput request input.
type GetMetadataInput struct {
	Path             string `json:"path"`
	IncludeMediaInfo bool   `json:"include_media_info"`
}

// GetMetadataOutput request output.
type GetMetadataOutput struct {
	Metadata
}

// GetMetadata returns the metadata for a file or folder.
func (c *Files) GetMetadata(in *GetMetadataInput) (out *GetMetadataOutput, err error) {
	body, err := c.call("/files/get_metadata", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// CreateFolderInput request input.
type CreateFolderInput struct {
	Path string `json:"path"`
}

// CreateFolderOutput request output.
type CreateFolderOutput struct {
	Name      string `json:"name"`
	PathLower string `json:"path_lower"`
	ID        string `json:"id"`
}

// CreateFolder creates a folder.
func (c *Files) CreateFolder(in *CreateFolderInput) (out *CreateFolderOutput, err error) {
	body, err := c.call("/files/create_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DeleteInput request input.
type DeleteInput struct {
	Path string `json:"path"`
}

// DeleteOutput request output.
type DeleteOutput struct {
	Metadata
}

// Delete a file or folder and its contents.
func (c *Files) Delete(in *DeleteInput) (out *DeleteOutput, err error) {
	body, err := c.call("/files/delete", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// PermanentlyDeleteInput request input.
type PermanentlyDeleteInput struct {
	Path string `json:"path"`
}

// PermanentlyDelete a file or folder and its contents.
func (c *Files) PermanentlyDelete(in *PermanentlyDeleteInput) (err error) {
	body, err := c.call("/files/delete", in)
	if err != nil {
		return
	}
	defer body.Close()

	return
}

// CopyInput request input.
type CopyInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// CopyOutput request output.
type CopyOutput struct {
	Metadata
}

// Copy a file or folder to a different location.
func (c *Files) Copy(in *CopyInput) (out *CopyOutput, err error) {
	body, err := c.call("/files/copy", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// MoveInput request input.
type MoveInput struct {
	FromPath string `json:"from_path"`
	ToPath   string `json:"to_path"`
}

// MoveOutput request output.
type MoveOutput struct {
	Metadata
}

// Move a file or folder to a different location.
func (c *Files) Move(in *MoveInput) (out *MoveOutput, err error) {
	body, err := c.call("/files/move", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// RestoreInput request input.
type RestoreInput struct {
	Path string `json:"path"`
	Rev  string `json:"rev"`
}

// RestoreOutput request output.
type RestoreOutput struct {
	Metadata
}

// Restore a file to a specific revision.
func (c *Files) Restore(in *RestoreInput) (out *RestoreOutput, err error) {
	body, err := c.call("/files/restore", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// ListFolderInput request input.
type ListFolderInput struct {
	Path             string `json:"path"`
	Recursive        bool   `json:"recursive"`
	IncludeMediaInfo bool   `json:"include_media_info"`
	IncludeDeleted   bool   `json:"include_deleted"`
}

// ListFolderOutput request output.
type ListFolderOutput struct {
	Cursor  string `json:"cursor"`
	HasMore bool   `json:"has_more"`
	Entries []*Metadata
}

// ListFolder returns the metadata for a file or folder.
func (c *Files) ListFolder(in *ListFolderInput) (out *ListFolderOutput, err error) {
	body, err := c.call("/files/list_folder", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// SearchMode determines how a search is performed.
type SearchMode string

// Supported search modes.
const (
	SearchModeFilename           SearchMode = "filename"
	SearchModeFilenameAndContent            = "filename_and_content"
	SearchModeDeletedFilename               = "deleted_filename"
)

// SearchMatch represents the type of match made.
type SearchMatchType string

// Supported search match types.
const (
	SearchMatchFilename SearchMatchType = "filename"
	SearchMatchContent                  = "content"
	SearchMatchBoth                     = "both"
)

// SearchMatch represents a matched file or folder.
type SearchMatch struct {
	MatchType struct {
		Tag SearchMatchType `json:".tag"`
	} `json:"match_type"`
	Metadata *Metadata `json:"metadata"`
}

// SearchInput request input.
type SearchInput struct {
	Path       string     `json:"path"`
	Query      string     `json:"query"`
	Start      uint64     `json:"start,omitempty"`
	MaxResults uint64     `json:"max_results,omitempty"`
	Mode       SearchMode `json:"mode"`
}

// SearchOutput request output.
type SearchOutput struct {
	Matches []*SearchMatch `json:"matches"`
	More    bool           `json:"more"`
	Start   uint64         `json:"start"`
}

// Search for files and folders.
func (c *Files) Search(in *SearchInput) (out *SearchOutput, err error) {
	if in.Mode == "" {
		in.Mode = SearchModeFilename
	}

	body, err := c.call("/files/search", in)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// UploadInput request input.
type UploadInput struct {
	Path           string    `json:"path"`
	Mode           WriteMode `json:"mode"`
	AutoRename     bool      `json:"autorename"`
	Mute           bool      `json:"mute"`
	ClientModified time.Time `json:"client_modified,omitempty"`
	Reader         io.Reader `json:"-"`
}

// UploadOutput request output.
type UploadOutput struct {
	Metadata
}

// Upload a file smaller than 150MB.
func (c *Files) Upload(in *UploadInput) (out *UploadOutput, err error) {
	body, err := c.download("/files/upload", in, in.Reader)
	if err != nil {
		return
	}
	defer body.Close()

	err = json.NewDecoder(body).Decode(&out)
	return
}

// DownloadInput request input.
type DownloadInput struct {
	Path string `json:"path"`
}

// DownloadOutput request output.
type DownloadOutput struct {
	Body io.ReadCloser
}

// Download a file.
func (c *Files) Download(in *DownloadInput) (out *DownloadOutput, err error) {
	body, err := c.download("/files/download", in, nil)
	if err != nil {
		return
	}

	out = &DownloadOutput{body}
	return
}

// GetPreviewInput request input.
type GetPreviewInput struct {
	Path string `json:"path"`
}

// GetPreviewOutput request output.
type GetPreviewOutput struct {
	Body io.ReadCloser
}

// GetPreview a preview for a file. Currently previews are only generated for the
// files with the following extensions: .doc, .docx, .docm, .ppt, .pps, .ppsx,
// .ppsm, .pptx, .pptm, .xls, .xlsx, .xlsm, .rtf
func (c *Files) GetPreview(in *GetPreviewInput) (out *GetPreviewOutput, err error) {
	body, err := c.download("/files/get_preview", in, nil)
	if err != nil {
		return
	}

	out = &GetPreviewOutput{body}
	return
}
<|endoftext|>