<commit_before>// jekyll is the configuration of handlers and such specific to the org's requirements. This is what you should copy and customize.
package jekyll

import (
	"github.com/parkr/auto-reply/affinity"
	"github.com/parkr/auto-reply/autopull"
	"github.com/parkr/auto-reply/chlog"
	"github.com/parkr/auto-reply/ctx"
	"github.com/parkr/auto-reply/hooks"
	"github.com/parkr/auto-reply/labeler"
	"github.com/parkr/auto-reply/lgtm"

	"github.com/parkr/auto-reply/jekyll/deprecate"
	"github.com/parkr/auto-reply/jekyll/issuecomment"
)

var lgtmEnabledRepos = []lgtm.Repo{
	{Owner: "jekyll", Name: "jekyll", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-coffeescript", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-compose", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-docs", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-feed", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-gist", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-import", Quorum: 1},
	{Owner: "jekyll", Name: "jekyll-mentions", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-opal", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-paginate", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-redirect-from", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-sass-converter", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-sitemap", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-textile-converter", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-watch", Quorum: 2},
	{Owner: "jekyll", Name: "github-metadata", Quorum: 2},
	{Owner: "jekyll", Name: "jemoji", Quorum: 1},
	{Owner: "jekyll", Name: "mercenary", Quorum: 1},
	{Owner: "jekyll", Name: "minima", Quorum: 1},
}

var jekyllOrgEventHandlers = map[hooks.EventType][]hooks.EventHandler{
	hooks.CreateEvent: {chlog.CreateReleaseOnTagHandler},
	hooks.IssuesEvent: {
		affinity.AssignIssueToAffinityTeamCaptain,
		deprecate.DeprecateOldRepos,
	},
	hooks.IssueCommentEvent: {
		affinity.AssignIssueToAffinityTeamCaptainFromComment,
		issuecomment.PendingFeedbackUnlabeler,
		issuecomment.StaleUnlabeler,
		chlog.MergeAndLabel,
		lgtm.NewIssueCommentHandler(lgtmEnabledRepos),
	},
	hooks.PushEvent: {autopull.AutomaticallyCreatePullRequest("jekyll/jekyll")},
	hooks.PullRequestEvent: {
		affinity.AssignPRToAffinityTeamCaptain,
		labeler.PendingRebaseNeedsWorkPRUnlabeler,
		lgtm.NewPullRequestHandler(lgtmEnabledRepos),
	},
}

func NewJekyllOrgHandler(context *ctx.Context) *hooks.GlobalHandler {
	affinity.Teams = []affinity.Team{
		affinity.Team{ID: 0, Name: "Build", Mention: "@jekyll/build"},
		affinity.Team{ID: 0, Name: "Documentation", Mention: "@jekyll/documentation"},
		affinity.Team{ID: 0, Name: "Ecosystem", Mention: "@jekyll/ecosystem"},
		affinity.Team{ID: 0, Name: "Performance", Mention: "@jekyll/performance"},
		affinity.Team{ID: 0, Name: "Stability", Mention: "@jekyll/stability"},
		affinity.Team{ID: 0, Name: "Windows", Mention: "@jekyll/windows"},
	}
	affinity.Repos = []affinity.Repo{
		{Owner: "jekyll", Name: "jekyll"},
	}
	return &hooks.GlobalHandler{
		Context:       context,
		EventHandlers: jekyllOrgEventHandlers,
	}
}
<commit_msg>Fix team IDs<commit_after>// jekyll is the configuration of handlers and such specific to the org's requirements. This is what you should copy and customize.
package jekyll

import (
	"github.com/parkr/auto-reply/affinity"
	"github.com/parkr/auto-reply/autopull"
	"github.com/parkr/auto-reply/chlog"
	"github.com/parkr/auto-reply/ctx"
	"github.com/parkr/auto-reply/hooks"
	"github.com/parkr/auto-reply/labeler"
	"github.com/parkr/auto-reply/lgtm"

	"github.com/parkr/auto-reply/jekyll/deprecate"
	"github.com/parkr/auto-reply/jekyll/issuecomment"
)

var lgtmEnabledRepos = []lgtm.Repo{
	{Owner: "jekyll", Name: "jekyll", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-coffeescript", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-compose", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-docs", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-feed", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-gist", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-import", Quorum: 1},
	{Owner: "jekyll", Name: "jekyll-mentions", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-opal", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-paginate", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-redirect-from", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-sass-converter", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-sitemap", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-textile-converter", Quorum: 2},
	{Owner: "jekyll", Name: "jekyll-watch", Quorum: 2},
	{Owner: "jekyll", Name: "github-metadata", Quorum: 2},
	{Owner: "jekyll", Name: "jemoji", Quorum: 1},
	{Owner: "jekyll", Name: "mercenary", Quorum: 1},
	{Owner: "jekyll", Name: "minima", Quorum: 1},
}

var jekyllOrgEventHandlers = map[hooks.EventType][]hooks.EventHandler{
	hooks.CreateEvent: {chlog.CreateReleaseOnTagHandler},
	hooks.IssuesEvent: {
		affinity.AssignIssueToAffinityTeamCaptain,
		deprecate.DeprecateOldRepos,
	},
	hooks.IssueCommentEvent: {
		affinity.AssignIssueToAffinityTeamCaptainFromComment,
		issuecomment.PendingFeedbackUnlabeler,
		issuecomment.StaleUnlabeler,
		chlog.MergeAndLabel,
		lgtm.NewIssueCommentHandler(lgtmEnabledRepos),
	},
	hooks.PushEvent: {autopull.AutomaticallyCreatePullRequest("jekyll/jekyll")},
	hooks.PullRequestEvent: {
		affinity.AssignPRToAffinityTeamCaptain,
		labeler.PendingRebaseNeedsWorkPRUnlabeler,
		lgtm.NewPullRequestHandler(lgtmEnabledRepos),
	},
}

func NewJekyllOrgHandler(context *ctx.Context) *hooks.GlobalHandler {
	affinity.Teams = []affinity.Team{
		affinity.Team{ID: 1961060, Name: "Build", Mention: "@jekyll/build"},
		affinity.Team{ID: 1961072, Name: "Documentation", Mention: "@jekyll/documentation"},
		affinity.Team{ID: 1961061, Name: "Ecosystem", Mention: "@jekyll/ecosystem"},
		affinity.Team{ID: 1961065, Name: "Performance", Mention: "@jekyll/performance"},
		affinity.Team{ID: 1961059, Name: "Stability", Mention: "@jekyll/stability"},
		affinity.Team{ID: 1116640, Name: "Windows", Mention: "@jekyll/windows"},
	}
	affinity.Repos = []affinity.Repo{
		{Owner: "jekyll", Name: "jekyll"},
	}
	return &hooks.GlobalHandler{
		Context:       context,
		EventHandlers: jekyllOrgEventHandlers,
	}
}
<|endoftext|>


<commit_before>package main

import (
    //"fmt"
    "image"
    "image/color"
    //"image/gif"
    "os"
    //"bufio"
    "io"
    "compress/lzw"
    "bytes"
    "math"
)

func writeHeader(w io.Writer, m image.Image) {
    b := m.Bounds()

    header := make([]byte, 0x320)

    header[0] = 'G'
    header[1] = 'I'
    header[2] = 'F'
    header[3] = '8'
    header[4] = '9'
    header[5] = 'a'

    header[7] = byte(b.Max.X / 255)
    header[6] = byte(b.Max.X % 255)
    header[9] = byte(b.Max.Y / 255)
    header[8] = byte(b.Max.Y % 255)

    header[0x0B] = byte(0x00) // Background color.
    header[0x0C] = byte(0x00) // Default pixel aspect ratio.

    // Grayscale color table.
    for i := byte(0); i < 255; i++ {
        header[0x0F + i] = i
        header[0x0E + i] = i
        header[0x0D + i] = i
    }

    header[0x30D] = byte(0x21) // GCE data header.
    header[0x30E] = byte(0xF9) // GCE data header (cont).
    header[0x30F] = byte(0x04) // Next 4 bytes are GCE data.
    header[0x310] = byte(0x01) // There is a transparent pixel.
    header[0x311] = byte(0x00) // Animation delay, LSB.
    header[0x312] = byte(0x00) // Animation delay, MSB.
    header[0x313] = byte(0x10) // And it is color #16 (0x10).
    header[0x314] = byte(0x00) // End of GCE data.

    header[0x315] = byte(0x2C) // Start of Image Descriptor.

    header[0x316] = byte(b.Min.X / 255)
    header[0x315] = byte(b.Min.X % 255)
    header[0x318] = byte(b.Min.Y / 255)
    header[0x317] = byte(b.Min.Y % 255)

    header[0x31B] = byte(b.Max.X / 255)
    header[0x31A] = byte(b.Max.X % 255)
    header[0x31D] = byte(b.Max.Y / 255)
    header[0x31C] = byte(b.Max.Y % 255)

    header[0x31E] = byte(0x00) // No local color table.

    header[0x31F] = byte(0x08) // Start of LZW with minimum code size 8.

    w.Write(header)
}

func compressImage(m image.Image) *bytes.Buffer {
    b := m.Bounds()

    compressedImageBuffer := bytes.NewBuffer(make([]byte, 0, 255))
    lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))

    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
            lzww.Write([]byte{c.Y})
            //lzww.Write([]byte{byte(x ^ y)})
            //lzww.Write([]byte{byte(0x00)})
        }
    }
    lzww.Close()

    return compressedImageBuffer
}

func writeBlocks(w io.Writer, compressedImage *bytes.Buffer) {
    const maxBlockSize = 255
    bytesSoFar := 0
    bytesRemaining := compressedImage.Len()
    for bytesRemaining > 0 {
        if bytesSoFar == 0 {
            blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
            w.Write([]byte{byte(blockSize)})
        }

        b, _ :=  compressedImage.ReadByte()
        w.Write([]byte{b})

        bytesSoFar = (bytesSoFar + 1) % maxBlockSize
        bytesRemaining--
    }
}

func Encode(w io.Writer, m image.Image) error {
    writeHeader(w, m)
    writeBlocks(w, compressImage(m))
    w.Write([]byte{0, ';'})

    return nil
}

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    m.Set(1, 1, color.RGBA{0x00, 0xFF, 0x00, 0xFF})
    file, _ := os.Create("new_image.gif")
    Encode(file, m)
}
<commit_msg>Fix global table generation<commit_after>package main

import (
    //"fmt"
    "image"
    "image/color"
    //"image/gif"
    "os"
    //"bufio"
    "io"
    "compress/lzw"
    "bytes"
    "math"
)

func writeHeader(w io.Writer, m image.Image) {
    b := m.Bounds()

    header := make([]byte, 0x320)

    header[0] = 'G'
    header[1] = 'I'
    header[2] = 'F'
    header[3] = '8'
    header[4] = '9'
    header[5] = 'a'

    header[7] = byte(b.Max.X / 255)
    header[6] = byte(b.Max.X % 255)
    header[9] = byte(b.Max.Y / 255)
    header[8] = byte(b.Max.Y % 255)

    header[0x0B] = byte(0x00) // Background color.
    header[0x0C] = byte(0x00) // Default pixel aspect ratio.

    // Grayscale color table.
    for i := 0; i < 255; i++ {
        header[0x0F + i * 3] = byte(i)
        header[0x0E + i * 3] = byte(i)
        header[0x0D + i * 3] = byte(i)
    }

    header[0x30D] = byte(0x21) // GCE data header.
    header[0x30E] = byte(0xF9) // GCE data header (cont).
    header[0x30F] = byte(0x04) // Next 4 bytes are GCE data.
    header[0x310] = byte(0x01) // There is a transparent pixel.
    header[0x311] = byte(0x00) // Animation delay, LSB.
    header[0x312] = byte(0x00) // Animation delay, MSB.
    header[0x313] = byte(0x10) // And it is color #16 (0x10).
    header[0x314] = byte(0x00) // End of GCE data.

    header[0x315] = byte(0x2C) // Start of Image Descriptor.

    header[0x316] = byte(b.Min.X / 255)
    header[0x315] = byte(b.Min.X % 255)
    header[0x318] = byte(b.Min.Y / 255)
    header[0x317] = byte(b.Min.Y % 255)

    header[0x31B] = byte(b.Max.X / 255)
    header[0x31A] = byte(b.Max.X % 255)
    header[0x31D] = byte(b.Max.Y / 255)
    header[0x31C] = byte(b.Max.Y % 255)

    header[0x31E] = byte(0x00) // No local color table.

    header[0x31F] = byte(0x08) // Start of LZW with minimum code size 8.

    w.Write(header)
}

func compressImage(m image.Image) *bytes.Buffer {
    b := m.Bounds()

    compressedImageBuffer := bytes.NewBuffer(make([]byte, 0, 255))
    lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))

    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
            lzww.Write([]byte{c.Y})
            //lzww.Write([]byte{byte(x ^ y)})
            //lzww.Write([]byte{byte(0x00)})
        }
    }
    lzww.Close()

    return compressedImageBuffer
}

func writeBlocks(w io.Writer, compressedImage *bytes.Buffer) {
    const maxBlockSize = 255
    bytesSoFar := 0
    bytesRemaining := compressedImage.Len()
    for bytesRemaining > 0 {
        if bytesSoFar == 0 {
            blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
            w.Write([]byte{byte(blockSize)})
        }

        b, _ :=  compressedImage.ReadByte()
        w.Write([]byte{b})

        bytesSoFar = (bytesSoFar + 1) % maxBlockSize
        bytesRemaining--
    }
}

func Encode(w io.Writer, m image.Image) error {
    writeHeader(w, m)
    writeBlocks(w, compressImage(m))
    w.Write([]byte{0, ';'})

    return nil
}

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    m.Set(1, 1, color.RGBA{0x00, 0xFF, 0x00, 0xFF})
    file, _ := os.Create("new_image.gif")
    Encode(file, m)
}
<|endoftext|>


<commit_before>package main

import (
    //"fmt"
    "image"
    "image/color"
    //"image/gif"
    "os"
    //"bufio"
    "io"
    "compress/lzw"
    "bytes"
    "math"
)

func writeHeader(w io.Writer, b image.Rectangle) {
    header := make([]byte, 0x320)

    header[0] = 'G'
    header[1] = 'I'
    header[2] = 'F'
    header[3] = '8'
    header[4] = '9'
    header[5] = 'a'

    header[7] = byte(b.Max.X / 255)
    header[6] = byte(b.Max.X % 255)
    header[9] = byte(b.Max.Y / 255)
    header[8] = byte(b.Max.Y % 255)

    header[0x0B] = byte(0x00) // Background color.
    header[0x0C] = byte(0x00) // Default pixel aspect ratio.

    // Grayscale color table.
    for i := byte(0); i < 255; i++ {
        header[0x0F + i] = i
        header[0x0E + i] = i
        header[0x0D + i] = i
    }

    header[0x30D] = byte(0x21) // GCE data header.
    header[0x30E] = byte(0xF9) // GCE data header (cont).
    header[0x30F] = byte(0x04) // Next 4 bytes are GCE data.
    header[0x310] = byte(0x01) // There is a transparent pixel.
    header[0x311] = byte(0x00) // Animation delay, LSB.
    header[0x312] = byte(0x00) // Animation delay, MSB.
    header[0x313] = byte(0x10) // And it is color #16 (0x10).
    header[0x314] = byte(0x00) // End of GCE data.

    header[0x315] = byte(0x2C) // Start of Image Descriptor.

    header[0x316] = byte(b.Min.X / 255)
    header[0x315] = byte(b.Min.X % 255)
    header[0x318] = byte(b.Min.Y / 255)
    header[0x317] = byte(b.Min.Y % 255)

    header[0x31B] = byte(b.Max.X / 255)
    header[0x31A] = byte(b.Max.X % 255)
    header[0x31D] = byte(b.Max.Y / 255)
    header[0x31C] = byte(b.Max.Y % 255)

    header[0x31E] = byte(0x00) // No local color table.

    header[0x31F] = byte(0x08) // Start of LZW with minimum code size 8.

    w.Write(header)
}

func Encode(w io.Writer, m image.Image) error {
    b := m.Bounds()
    writeHeader(w, b)

    compressedImageBuffer := bytes.NewBuffer(make([]byte, 0, 255))
    lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))

    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
            lzww.Write([]byte{c.Y})
            //lzww.Write([]byte{byte(x ^ y)})
            //lzww.Write([]byte{byte(0x00)})
        }
    }
    lzww.Close()

    const maxBlockSize = 255
    bytesSoFar := 0
    bytesRemaining := compressedImageBuffer.Len()
    for bytesRemaining > 0 {
        if bytesSoFar == 0 {
            blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
            w.Write([]byte{byte(blockSize)})
        }

        b, _ :=  compressedImageBuffer.ReadByte()
        w.Write([]byte{b})

        bytesSoFar = (bytesSoFar + 1) % maxBlockSize
        bytesRemaining--
    }

    w.Write([]byte{0, ';'})

    return nil
}

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    m.Set(1, 1, color.RGBA{0x00, 0xFF, 0x00, 0xFF})
    file, _ := os.Create("new_image.gif")
    Encode(file, m)
}
<commit_msg>Move image compression to separate function<commit_after>package main

import (
    //"fmt"
    "image"
    "image/color"
    //"image/gif"
    "os"
    //"bufio"
    "io"
    "compress/lzw"
    "bytes"
    "math"
)

func writeHeader(w io.Writer, m image.Image) {
    b := m.Bounds()

    header := make([]byte, 0x320)

    header[0] = 'G'
    header[1] = 'I'
    header[2] = 'F'
    header[3] = '8'
    header[4] = '9'
    header[5] = 'a'

    header[7] = byte(b.Max.X / 255)
    header[6] = byte(b.Max.X % 255)
    header[9] = byte(b.Max.Y / 255)
    header[8] = byte(b.Max.Y % 255)

    header[0x0B] = byte(0x00) // Background color.
    header[0x0C] = byte(0x00) // Default pixel aspect ratio.

    // Grayscale color table.
    for i := byte(0); i < 255; i++ {
        header[0x0F + i] = i
        header[0x0E + i] = i
        header[0x0D + i] = i
    }

    header[0x30D] = byte(0x21) // GCE data header.
    header[0x30E] = byte(0xF9) // GCE data header (cont).
    header[0x30F] = byte(0x04) // Next 4 bytes are GCE data.
    header[0x310] = byte(0x01) // There is a transparent pixel.
    header[0x311] = byte(0x00) // Animation delay, LSB.
    header[0x312] = byte(0x00) // Animation delay, MSB.
    header[0x313] = byte(0x10) // And it is color #16 (0x10).
    header[0x314] = byte(0x00) // End of GCE data.

    header[0x315] = byte(0x2C) // Start of Image Descriptor.

    header[0x316] = byte(b.Min.X / 255)
    header[0x315] = byte(b.Min.X % 255)
    header[0x318] = byte(b.Min.Y / 255)
    header[0x317] = byte(b.Min.Y % 255)

    header[0x31B] = byte(b.Max.X / 255)
    header[0x31A] = byte(b.Max.X % 255)
    header[0x31D] = byte(b.Max.Y / 255)
    header[0x31C] = byte(b.Max.Y % 255)

    header[0x31E] = byte(0x00) // No local color table.

    header[0x31F] = byte(0x08) // Start of LZW with minimum code size 8.

    w.Write(header)
}

func compressImage(m image.Image) *bytes.Buffer {
    b := m.Bounds()

    compressedImageBuffer := bytes.NewBuffer(make([]byte, 0, 255))
    lzww := lzw.NewWriter(compressedImageBuffer, lzw.LSB, int(8))

    for y := b.Min.Y; y < b.Max.Y; y++ {
        for x := b.Min.X; x < b.Max.X; x++ {
            c := color.GrayModel.Convert(m.At(x, y)).(color.Gray)
            lzww.Write([]byte{c.Y})
            //lzww.Write([]byte{byte(x ^ y)})
            //lzww.Write([]byte{byte(0x00)})
        }
    }
    lzww.Close()

    return compressedImageBuffer
}

func Encode(w io.Writer, m image.Image) error {
    writeHeader(w, m)

    compressedImage := compressImage(m)

    const maxBlockSize = 255
    bytesSoFar := 0
    bytesRemaining := compressedImage.Len()
    for bytesRemaining > 0 {
        if bytesSoFar == 0 {
            blockSize := math.Min(maxBlockSize, float64(bytesRemaining))
            w.Write([]byte{byte(blockSize)})
        }

        b, _ :=  compressedImage.ReadByte()
        w.Write([]byte{b})

        bytesSoFar = (bytesSoFar + 1) % maxBlockSize
        bytesRemaining--
    }

    w.Write([]byte{0, ';'})

    return nil
}

func main() {
    m := image.NewRGBA(image.Rect(0, 0, 100, 100))
    m.Set(1, 1, color.RGBA{0x00, 0xFF, 0x00, 0xFF})
    file, _ := os.Create("new_image.gif")
    Encode(file, m)
}
<|endoftext|>


<commit_before>package tcp

import (
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	ErrConnClosing = errors.New("use of closed network connection")
	ErrBufferFull  = errors.New("the async send buffer is full")
)

type TCPConn struct {
	callback CallBack
	protocol Protocol

	conn      *net.TCPConn
	readChan  chan Packet
	writeChan chan Packet

	readDeadline  time.Duration
	writeDeadline time.Duration

	exitChan  chan struct{}
	closeOnce sync.Once
	exitFlag  int32
	extraData map[string]interface{}
}

func NewTCPConn(conn *net.TCPConn, callback CallBack, protocol Protocol) *TCPConn {
	c := &TCPConn{
		conn:     conn,
		callback: callback,
		protocol: protocol,

		readChan:  make(chan Packet, readChanSize),
		writeChan: make(chan Packet, writeChanSize),

		exitChan: make(chan struct{}),
		exitFlag: 0,
	}
	return c
}

func (c *TCPConn) Serve() error {
	defer func() {
		if r := recover(); r != nil {
			logger.Println("tcp conn(%v) Serve error, %v ", c.GetRemoteIPAddress(), r)
		}
	}()
	if c.callback == nil || c.protocol == nil {
		err := fmt.Errorf("callback and protocol are not allowed to be nil")
		c.Close()
		return err
	}
	atomic.StoreInt32(&c.exitFlag, 1)
	c.callback.OnConnected(c)
	go c.readLoop()
	go c.writeLoop()
	go c.handleLoop()
	return nil
}

func (c *TCPConn) readLoop() {
	defer func() {
		recover()
		c.Close()
	}()

	for {
		select {
		case <-c.exitChan:
			return
		default:
			if c.readDeadline > 0 {
				c.conn.SetReadDeadline(time.Now().Add(c.readDeadline))
			}
			p, err := c.protocol.ReadPacket(c.conn)
			if err != nil {
				if err != io.EOF {
					c.callback.OnError(err)
				}
				return
			}
			c.readChan <- p
		}
	}
}

func (c *TCPConn) ReadPacket() (Packet, error) {
	if c.protocol == nil {
		return nil, errors.New("no protocol impl")
	}
	return c.protocol.ReadPacket(c.conn)
}

func (c *TCPConn) writeLoop() {
	defer func() {
		recover()
		c.Close()
	}()

	for pkt := range c.writeChan {
		if pkt == nil {
			continue
		}
		if c.writeDeadline > 0 {
			c.conn.SetWriteDeadline(time.Now().Add(c.writeDeadline))
		}
		if err := c.protocol.WritePacket(c.conn, pkt); err != nil {
			c.callback.OnError(err)
			return
		}
	}
}

func (c *TCPConn) handleLoop() {
	defer func() {
		recover()
		c.Close()
	}()
	for p := range c.readChan {
		if p == nil {
			continue
		}
		c.callback.OnMessage(c, p)
	}
}

func (c *TCPConn) AsyncWritePacket(p Packet) error {
	if c.IsClosed() {
		return ErrConnClosing
	}
	select {
	case c.writeChan <- p:
		return nil
	default:
		return ErrBufferFull
	}
}

func (c *TCPConn) AsyncWritePacketWithTimeout(p Packet, sec int) error {
	if c.IsClosed() {
		return ErrConnClosing
	}
	select {
	case c.writeChan <- p:
		return nil
	case <-time.After(time.Second * time.Duration(sec)):
		return ErrBufferFull
	}
}

func (c *TCPConn) Close() {
	c.closeOnce.Do(func() {
		atomic.StoreInt32(&c.exitFlag, 0)
		close(c.exitChan)
		close(c.writeChan)
		close(c.readChan)
		if c.callback != nil {
			c.callback.OnDisconnected(c)
		}
		c.conn.Close()
	})
}

func (c *TCPConn) GetRawConn() *net.TCPConn {
	return c.conn
}

func (c *TCPConn) IsClosed() bool {
	return atomic.LoadInt32(&c.exitFlag) == 0
}

func (c *TCPConn) GetLocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

//LocalIPAddress 返回socket连接本地的ip地址
func (c *TCPConn) GetLocalIPAddress() string {
	return strings.Split(c.GetLocalAddr().String(), ":")[0]
}

func (c *TCPConn) GetRemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (c *TCPConn) GetRemoteIPAddress() string {
	return strings.Split(c.GetRemoteAddr().String(), ":")[0]
}

func (c *TCPConn) setReadDeadline(t time.Duration) {
	c.readDeadline = t
}

func (c *TCPConn) setWriteDeadline(t time.Duration) {
	c.writeDeadline = t
}

func (c *TCPConn) SetExtraData(key string, data interface{}) {
	if c.extraData == nil {
		c.extraData = make(map[string]interface{})
	}
	c.extraData[key] = data
}

func (c *TCPConn) GetExtraData(key string) interface{} {
	if data, ok := c.extraData[key]; ok {
		return data
	}
	return nil
}
<commit_msg>add new error<commit_after>package tcp

import (
	"errors"
	"fmt"
	"io"
	"net"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var (
	ErrConnClosing  = errors.New("use of closed network connection")
	ErrBufferFull   = errors.New("the async send buffer is full")
	ErrWriteTimeout = errors.New("async write packet timeout")
)

type TCPConn struct {
	callback CallBack
	protocol Protocol

	conn      *net.TCPConn
	readChan  chan Packet
	writeChan chan Packet

	readDeadline  time.Duration
	writeDeadline time.Duration

	exitChan  chan struct{}
	closeOnce sync.Once
	exitFlag  int32
	extraData map[string]interface{}
}

func NewTCPConn(conn *net.TCPConn, callback CallBack, protocol Protocol) *TCPConn {
	c := &TCPConn{
		conn:     conn,
		callback: callback,
		protocol: protocol,

		readChan:  make(chan Packet, readChanSize),
		writeChan: make(chan Packet, writeChanSize),

		exitChan: make(chan struct{}),
		exitFlag: 0,
	}
	return c
}

func (c *TCPConn) Serve() error {
	defer func() {
		if r := recover(); r != nil {
			logger.Println("tcp conn(%v) Serve error, %v ", c.GetRemoteIPAddress(), r)
		}
	}()
	if c.callback == nil || c.protocol == nil {
		err := fmt.Errorf("callback and protocol are not allowed to be nil")
		c.Close()
		return err
	}
	atomic.StoreInt32(&c.exitFlag, 1)
	c.callback.OnConnected(c)
	go c.readLoop()
	go c.writeLoop()
	go c.handleLoop()
	return nil
}

func (c *TCPConn) readLoop() {
	defer func() {
		recover()
		c.Close()
	}()

	for {
		select {
		case <-c.exitChan:
			return
		default:
			if c.readDeadline > 0 {
				c.conn.SetReadDeadline(time.Now().Add(c.readDeadline))
			}
			p, err := c.protocol.ReadPacket(c.conn)
			if err != nil {
				if err != io.EOF {
					c.callback.OnError(err)
				}
				return
			}
			c.readChan <- p
		}
	}
}

func (c *TCPConn) ReadPacket() (Packet, error) {
	if c.protocol == nil {
		return nil, errors.New("no protocol impl")
	}
	return c.protocol.ReadPacket(c.conn)
}

func (c *TCPConn) writeLoop() {
	defer func() {
		recover()
		c.Close()
	}()

	for pkt := range c.writeChan {
		if pkt == nil {
			continue
		}
		if c.writeDeadline > 0 {
			c.conn.SetWriteDeadline(time.Now().Add(c.writeDeadline))
		}
		if err := c.protocol.WritePacket(c.conn, pkt); err != nil {
			c.callback.OnError(err)
			return
		}
	}
}

func (c *TCPConn) handleLoop() {
	defer func() {
		recover()
		c.Close()
	}()
	for p := range c.readChan {
		if p == nil {
			continue
		}
		c.callback.OnMessage(c, p)
	}
}

func (c *TCPConn) AsyncWritePacket(p Packet) error {
	if c.IsClosed() {
		return ErrConnClosing
	}
	select {
	case c.writeChan <- p:
		return nil
	default:
		return ErrBufferFull
	}
}

func (c *TCPConn) AsyncWritePacketWithTimeout(p Packet, sec int) error {
	if c.IsClosed() {
		return ErrConnClosing
	}
	select {
	case c.writeChan <- p:
		return nil
	case <-time.After(time.Second * time.Duration(sec)):
		return ErrWriteTimeout
	}
}

func (c *TCPConn) Close() {
	c.closeOnce.Do(func() {
		atomic.StoreInt32(&c.exitFlag, 0)
		close(c.exitChan)
		close(c.writeChan)
		close(c.readChan)
		if c.callback != nil {
			c.callback.OnDisconnected(c)
		}
		c.conn.Close()
	})
}

func (c *TCPConn) GetRawConn() *net.TCPConn {
	return c.conn
}

func (c *TCPConn) IsClosed() bool {
	return atomic.LoadInt32(&c.exitFlag) == 0
}

func (c *TCPConn) GetLocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

//LocalIPAddress 返回socket连接本地的ip地址
func (c *TCPConn) GetLocalIPAddress() string {
	return strings.Split(c.GetLocalAddr().String(), ":")[0]
}

func (c *TCPConn) GetRemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (c *TCPConn) GetRemoteIPAddress() string {
	return strings.Split(c.GetRemoteAddr().String(), ":")[0]
}

func (c *TCPConn) setReadDeadline(t time.Duration) {
	c.readDeadline = t
}

func (c *TCPConn) setWriteDeadline(t time.Duration) {
	c.writeDeadline = t
}

func (c *TCPConn) SetExtraData(key string, data interface{}) {
	if c.extraData == nil {
		c.extraData = make(map[string]interface{})
	}
	c.extraData[key] = data
}

func (c *TCPConn) GetExtraData(key string) interface{} {
	if data, ok := c.extraData[key]; ok {
		return data
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

func writeTemplates(dir, gopkg, debsrc, debbin, debversion, pkgType string, dependencies []string, vendorDirs []string) error {
	if err := os.Mkdir(filepath.Join(dir, "debian"), 0755); err != nil {
		return err
	}

	if err := os.Mkdir(filepath.Join(dir, "debian", "source"), 0755); err != nil {
		return err
	}

	f, err := os.Create(filepath.Join(dir, "debian", "changelog"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "%s (%s) UNRELEASED; urgency=medium\n", debsrc, debversion)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "  * Initial release (Closes: TODO)\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, " -- %s <%s>  %s\n",
		getDebianName(),
		getDebianEmail(),
		time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700"))

	f, err = os.Create(filepath.Join(dir, "debian", "control"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "Source: %s\n", debsrc)
	fmt.Fprintf(f, "Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>\n")
	fprintfControlField(f, "Uploaders", []string{getDebianName() + " <" + getDebianEmail() + ">"})
	// TODO: change this once we have a “golang” section.
	fmt.Fprintf(f, "Section: devel\n")
	fmt.Fprintf(f, "Testsuite: autopkgtest-pkg-go\n")
	fmt.Fprintf(f, "Priority: optional\n")
	builddeps := []string{"debhelper-compat (= 12)", "dh-golang"}
	builddepsByType := append([]string{"golang-any"}, dependencies...)
	sort.Strings(builddepsByType)
	fprintfControlField(f, "Build-Depends", builddeps)
	builddepsDepType := "Indep"
	if pkgType == "program" {
		builddepsDepType = "Arch"
	}
	fprintfControlField(f, "Build-Depends-"+builddepsDepType, builddepsByType)
	fmt.Fprintf(f, "Standards-Version: 4.4.1\n")
	fmt.Fprintf(f, "Vcs-Browser: https://salsa.debian.org/go-team/packages/%s\n", debsrc)
	fmt.Fprintf(f, "Vcs-Git: https://salsa.debian.org/go-team/packages/%s.git\n", debsrc)
	fmt.Fprintf(f, "Homepage: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Rules-Requires-Root: no\n")
	fmt.Fprintf(f, "XS-Go-Import-Path: %s\n", gopkg)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Package: %s\n", debbin)
	deps := []string{"${misc:Depends}"}
	if pkgType == "program" {
		fmt.Fprintf(f, "Architecture: any\n")
		deps = append(deps, "${shlibs:Depends}")
	} else {
		fmt.Fprintf(f, "Architecture: all\n")
		deps = append(deps, dependencies...)
	}
	sort.Strings(deps)
	fprintfControlField(f, "Depends", deps)
	if pkgType == "program" {
		fmt.Fprintf(f, "Built-Using: ${misc:Built-Using}\n")
	}
	description, err := getDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine description for %q: %v\n", gopkg, err)
		description = "TODO: short description"
	}
	fmt.Fprintf(f, "Description: %s\n", description)
	longdescription, err := getLongDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine long description for %q: %v\n", gopkg, err)
		longdescription = "TODO: long description"
	}
	fmt.Fprintf(f, " %s\n", longdescription)

	license, fulltext, err := getLicenseForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine license for %q: %v\n", gopkg, err)
		license = "TODO"
		fulltext = "TODO"
	}
	f, err = os.Create(filepath.Join(dir, "debian", "copyright"))
	if err != nil {
		return err
	}
	defer f.Close()
	_, copyright, err := getAuthorAndCopyrightForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine copyright for %q: %v\n", gopkg, err)
		copyright = "TODO"
	}
	fmt.Fprintf(f, "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n")
	fmt.Fprintf(f, "Source: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Upstream-Name: %s\n", filepath.Base(gopkg))
	fmt.Fprintf(f, "Files-Excluded:\n")
	for _, dir := range vendorDirs {
		fmt.Fprintf(f, " %s\n", dir)
	}
	fmt.Fprintf(f, " Godeps/_workspace\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:\n *\n")
	fmt.Fprintf(f, "Copyright:\n %s\n", copyright)
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:\n debian/*\n")
	fmt.Fprintf(f, "Copyright:\n %s %s <%s>\n", time.Now().Format("2006"), getDebianName(), getDebianEmail())
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "Comment: Debian packaging is licensed under the same terms as upstream\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, fulltext)

	f, err = os.Create(filepath.Join(dir, "debian", "rules"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "#!/usr/bin/make -f\n")
	fmt.Fprintf(f, "\n")
	if pkgType == "program" {
		fmt.Fprintf(f, "override_dh_auto_install:\n")
		fmt.Fprintf(f, "\tdh_auto_install -- --no-source\n")
		fmt.Fprintf(f, "\n")
	}
	fmt.Fprintf(f, "%%:\n")
	fmt.Fprintf(f, "\tdh $@ --builddirectory=_build --buildsystem=golang --with=golang\n")

	f, err = os.Create(filepath.Join(dir, "debian", "source", "format"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "3.0 (quilt)\n")

	f, err = os.Create(filepath.Join(dir, "debian", "gbp.conf"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "[DEFAULT]\n")

	if dep14 {
		fmt.Fprintf(f, "debian-branch = debian/sid\n")
	}

	if pristineTar {
		fmt.Fprintf(f, "pristine-tar = True\n")
	}

	if err := os.Chmod(filepath.Join(dir, "debian", "rules"), 0755); err != nil {
		return err
	}

	if strings.HasPrefix(gopkg, "github.com/") {
		f, err = os.Create(filepath.Join(dir, "debian", "watch"))
		if err != nil {
			return err
		}
		defer f.Close()
		fmt.Fprintf(f, "version=4\n")
		fmt.Fprintf(f, `opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/%s-\$1\.tar\.gz/,\`+"\n", debsrc)
		fmt.Fprintf(f, `uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \`+"\n")
		fmt.Fprintf(f, `  https://%s/tags .*/v?(\d\S*)\.tar\.gz`+"\n", gopkg)
	}

	return nil
}

func fprintfControlField(f *os.File, field string, valueArray []string) {
	switch wrapAndSort {
	case "a":
		// Current default, also what "cme fix dpkg" generates
		fmt.Fprintf(f, "%s: %s\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "at", "ta":
		// -t, --trailing-comma, preferred by Martina Ferrari
		// and currently used in quite a few packages
		fmt.Fprintf(f, "%s: %s,\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "ast", "ats", "sat", "sta", "tas", "tsa":
		// -s, --short-indent too, proposed by Guillem Jover
		fmt.Fprintf(f, "%s:\n %s,\n", field, strings.Join(valueArray, ",\n "))
	default:
		log.Fatalf("%q is not a valid value for -wrap-and-sort, aborting.", wrapAndSort)
	}
}
<commit_msg>template: Move override rule to the end of debian/rules<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

func writeTemplates(dir, gopkg, debsrc, debbin, debversion, pkgType string, dependencies []string, vendorDirs []string) error {
	if err := os.Mkdir(filepath.Join(dir, "debian"), 0755); err != nil {
		return err
	}

	if err := os.Mkdir(filepath.Join(dir, "debian", "source"), 0755); err != nil {
		return err
	}

	f, err := os.Create(filepath.Join(dir, "debian", "changelog"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "%s (%s) UNRELEASED; urgency=medium\n", debsrc, debversion)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "  * Initial release (Closes: TODO)\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, " -- %s <%s>  %s\n",
		getDebianName(),
		getDebianEmail(),
		time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700"))

	f, err = os.Create(filepath.Join(dir, "debian", "control"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "Source: %s\n", debsrc)
	fmt.Fprintf(f, "Maintainer: Debian Go Packaging Team <team+pkg-go@tracker.debian.org>\n")
	fprintfControlField(f, "Uploaders", []string{getDebianName() + " <" + getDebianEmail() + ">"})
	// TODO: change this once we have a “golang” section.
	fmt.Fprintf(f, "Section: devel\n")
	fmt.Fprintf(f, "Testsuite: autopkgtest-pkg-go\n")
	fmt.Fprintf(f, "Priority: optional\n")
	builddeps := []string{"debhelper-compat (= 12)", "dh-golang"}
	builddepsByType := append([]string{"golang-any"}, dependencies...)
	sort.Strings(builddepsByType)
	fprintfControlField(f, "Build-Depends", builddeps)
	builddepsDepType := "Indep"
	if pkgType == "program" {
		builddepsDepType = "Arch"
	}
	fprintfControlField(f, "Build-Depends-"+builddepsDepType, builddepsByType)
	fmt.Fprintf(f, "Standards-Version: 4.4.1\n")
	fmt.Fprintf(f, "Vcs-Browser: https://salsa.debian.org/go-team/packages/%s\n", debsrc)
	fmt.Fprintf(f, "Vcs-Git: https://salsa.debian.org/go-team/packages/%s.git\n", debsrc)
	fmt.Fprintf(f, "Homepage: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Rules-Requires-Root: no\n")
	fmt.Fprintf(f, "XS-Go-Import-Path: %s\n", gopkg)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Package: %s\n", debbin)
	deps := []string{"${misc:Depends}"}
	if pkgType == "program" {
		fmt.Fprintf(f, "Architecture: any\n")
		deps = append(deps, "${shlibs:Depends}")
	} else {
		fmt.Fprintf(f, "Architecture: all\n")
		deps = append(deps, dependencies...)
	}
	sort.Strings(deps)
	fprintfControlField(f, "Depends", deps)
	if pkgType == "program" {
		fmt.Fprintf(f, "Built-Using: ${misc:Built-Using}\n")
	}
	description, err := getDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine description for %q: %v\n", gopkg, err)
		description = "TODO: short description"
	}
	fmt.Fprintf(f, "Description: %s\n", description)
	longdescription, err := getLongDescriptionForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine long description for %q: %v\n", gopkg, err)
		longdescription = "TODO: long description"
	}
	fmt.Fprintf(f, " %s\n", longdescription)

	license, fulltext, err := getLicenseForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine license for %q: %v\n", gopkg, err)
		license = "TODO"
		fulltext = "TODO"
	}
	f, err = os.Create(filepath.Join(dir, "debian", "copyright"))
	if err != nil {
		return err
	}
	defer f.Close()
	_, copyright, err := getAuthorAndCopyrightForGopkg(gopkg)
	if err != nil {
		log.Printf("Could not determine copyright for %q: %v\n", gopkg, err)
		copyright = "TODO"
	}
	fmt.Fprintf(f, "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n")
	fmt.Fprintf(f, "Source: %s\n", getHomepageForGopkg(gopkg))
	fmt.Fprintf(f, "Upstream-Name: %s\n", filepath.Base(gopkg))
	fmt.Fprintf(f, "Files-Excluded:\n")
	for _, dir := range vendorDirs {
		fmt.Fprintf(f, " %s\n", dir)
	}
	fmt.Fprintf(f, " Godeps/_workspace\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:\n *\n")
	fmt.Fprintf(f, "Copyright:\n %s\n", copyright)
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "Files:\n debian/*\n")
	fmt.Fprintf(f, "Copyright:\n %s %s <%s>\n", time.Now().Format("2006"), getDebianName(), getDebianEmail())
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, "Comment: Debian packaging is licensed under the same terms as upstream\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "License: %s\n", license)
	fmt.Fprintf(f, fulltext)

	f, err = os.Create(filepath.Join(dir, "debian", "rules"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "#!/usr/bin/make -f\n")
	fmt.Fprintf(f, "\n")
	fmt.Fprintf(f, "%%:\n")
	fmt.Fprintf(f, "\tdh $@ --builddirectory=_build --buildsystem=golang --with=golang\n")
	if pkgType == "program" {
		fmt.Fprintf(f, "\n")
		fmt.Fprintf(f, "override_dh_auto_install:\n")
		fmt.Fprintf(f, "\tdh_auto_install -- --no-source\n")
	}

	f, err = os.Create(filepath.Join(dir, "debian", "source", "format"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "3.0 (quilt)\n")

	f, err = os.Create(filepath.Join(dir, "debian", "gbp.conf"))
	if err != nil {
		return err
	}
	defer f.Close()
	fmt.Fprintf(f, "[DEFAULT]\n")

	if dep14 {
		fmt.Fprintf(f, "debian-branch = debian/sid\n")
	}

	if pristineTar {
		fmt.Fprintf(f, "pristine-tar = True\n")
	}

	if err := os.Chmod(filepath.Join(dir, "debian", "rules"), 0755); err != nil {
		return err
	}

	if strings.HasPrefix(gopkg, "github.com/") {
		f, err = os.Create(filepath.Join(dir, "debian", "watch"))
		if err != nil {
			return err
		}
		defer f.Close()
		fmt.Fprintf(f, "version=4\n")
		fmt.Fprintf(f, `opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/%s-\$1\.tar\.gz/,\`+"\n", debsrc)
		fmt.Fprintf(f, `uversionmangle=s/(\d)[_\.\-\+]?(RC|rc|pre|dev|beta|alpha)[.]?(\d*)$/\$1~\$2\$3/ \`+"\n")
		fmt.Fprintf(f, `  https://%s/tags .*/v?(\d\S*)\.tar\.gz`+"\n", gopkg)
	}

	return nil
}

func fprintfControlField(f *os.File, field string, valueArray []string) {
	switch wrapAndSort {
	case "a":
		// Current default, also what "cme fix dpkg" generates
		fmt.Fprintf(f, "%s: %s\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "at", "ta":
		// -t, --trailing-comma, preferred by Martina Ferrari
		// and currently used in quite a few packages
		fmt.Fprintf(f, "%s: %s,\n", field, strings.Join(valueArray, ",\n"+strings.Repeat(" ", len(field)+2)))
	case "ast", "ats", "sat", "sta", "tas", "tsa":
		// -s, --short-indent too, proposed by Guillem Jover
		fmt.Fprintf(f, "%s:\n %s,\n", field, strings.Join(valueArray, ",\n "))
	default:
		log.Fatalf("%q is not a valid value for -wrap-and-sort, aborting.", wrapAndSort)
	}
}
<|endoftext|>


<commit_before>package transaction

import (
	"math"
	"time"

	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/saturn/config"
	lproto "github.com/levenlabs/saturn/proto"
)

// IncomingMessage takes a message just received from the remote address and
// processes it. It may return a message which should be sent back to the
// remote, or it may return nil. It also returns bool signaling to end
// the transaction if true.
func IncomingMessage(msg *lproto.TxMsg) (*lproto.TxMsg, bool) {
	im := incomingMsg{
		msg:     msg,
		replyCh: make(chan incomingReply),
	}
	incomingMsgCh <- im
	r := <-im.replyCh
	return r.nextMsg, r.end
}

type incomingMsg struct {
	msg     *lproto.TxMsg
	replyCh chan incomingReply
}

type incomingReply struct {
	nextMsg *lproto.TxMsg
	end     bool
}

var incomingMsgCh = make(chan incomingMsg)

// returns the next message, if any, and a bool indiciating if the transaction
// should be considered ended or not
func incoming(transactions map[string]*tx, msg *lproto.TxMsg) (*lproto.TxMsg, bool) {

	kv := llog.KV{
		"msg": msg,
	}
	if !msg.Valid() {
		llog.Warn("received invalid message (possibly from a newer version?)", kv)
		return nil, false
	}
	if !msg.Verify() {
		llog.Warn("received incorrectly signed message", kv)
		return nil, false
	}

	t := transactions[msg.Id]
	if t != nil && t.expectedSeq != msg.Seq {
		kv["txExpectedSeq"] = t.expectedSeq
		kv["txReceivedSeq"] = msg.Seq
		llog.Warn("received message with wrong seq", kv)
		// end the transaction since we got out of order
		return nil, true
	}

	nextSeq := msg.Seq + 1
	retTx := &lproto.TxMsg{
		Id:  msg.Id,
		Seq: nextSeq,
	}

	var hasInner bool
	var ended bool
	if r := msg.GetInitialReport(); r != nil {
		retTx.Inner = handleIncomingInitialReport(transactions, msg.Id, r)
		hasInner = true

	} else if r := msg.GetReport(); r != nil {
		if t == nil {
			llog.Warn("received Report for unknown transaction", kv)
			// we don't know about this transaction anymore so end
			return nil, true
		}
		iR, iF := handleIncomingReport(t, r)
		if iR != nil {
			retTx.Inner = iR
		} else {
			retTx.Inner = iF
			// since this is a fin, we can destroy this transaction after
			// writing
			ended = true
			defer func() {
				cleanTx(transactions, t.id)
			}()
		}
		hasInner = true

	} else if f := msg.GetFin(); f != nil {
		if t == nil {
			llog.Warn("received Fin for unknown transaction", kv)
			// we don't know about this transaction anymore so end
			return nil, true
		}
		handleFin(transactions, t, f)
		ended = true

	} else {
		llog.Warn("received unknown proto.Message type", kv)
	}

	if hasInner {
		// We re-get t because the transaction might not have existed when t was
		// created, although it definitely exists now
		t = transactions[msg.Id]
		// Add 2 here since the next seq should be the next incoming one which is
		// one greater than the one we just sent
		t.expectedSeq = nextSeq + 1
		t.lastMessage = time.Now()

		retTx.Sign()
	} else {
		retTx = nil
	}
	return retTx, ended
}

// This will only occur on the master's side, the slave will only ever be
// sending an initial report, never receiving one
func handleIncomingInitialReport(transactions map[string]*tx, id string, rep *lproto.InitialReport) *lproto.TxMsg_Report {
	t := newTx(transactions, id, rep.Name)

	now := time.Now()
	diff := time.Duration(rep.Time - now.UnixNano())
	kv := t.kv()
	llog.Debug("incoming initial report", kv, llog.KV{"isMaster": config.IsMaster})

	//store the first offset and we'll calculate the trip time when we get the
	//first report
	t.offsets = append(t.offsets, diff)

	return &lproto.TxMsg_Report{
		Report: &lproto.Report{
			Diff: int64(diff),
			Time: now.UnixNano(),
		},
	}
}

func handleIncomingReport(t *tx, rep *lproto.Report) (*lproto.TxMsg_Report, *lproto.TxMsg_Fin) {
	now := time.Now()
	diff := time.Duration(rep.Time - now.UnixNano())
	kv := t.kv()
	llog.Debug("incoming report", kv, llog.KV{"isMaster": config.IsMaster})

	if config.IsMaster {
		//if this is the 3rd packet then we need to include the RTT for the first offest
		if len(t.offsets) == 1 && len(t.tripTimes) == 0 {
			t.tripTimes = append(t.tripTimes, time.Duration(rep.Diff)-t.offsets[0])
		}
		t.tripTimes = append(t.tripTimes, now.Sub(t.lastMessage))
		t.offsets = append(t.offsets, diff)
		kv["txNumTrips"] = len(t.tripTimes)

		//first trip is free and then each iteration is 2 trips
		//seq starts at 1 so after 1 iteration it'll be at 3
		//only the master can terminate a sequence
		if (t.expectedSeq / 2) >= config.Iterations {
			offset, err := calculateAverageOffset(t.tripTimes, t.offsets)
			fin := &lproto.Fin{}
			if err != nil {
				fin.Error = err.Error()
				llog.Error("error calculating avg offset", kv.Set("err", err))
			} else {
				fin.Offset = offset
				kv["offset"] = offset
				llog.Info("slave offset", kv)
				if config.Threshold < math.Abs(offset) {
					llog.Warn("slave offset is over threshold", kv)
				}
			}
			llog.Debug("over iterations, ending transaction", kv)
			return nil, &lproto.TxMsg_Fin{
				Fin: fin,
			}
		}
	}

	return &lproto.TxMsg_Report{
		Report: &lproto.Report{
			Diff: int64(diff),
			Time: now.UnixNano(),
		},
	}, nil
}

func handleFin(transactions map[string]*tx, t *tx, fin *lproto.Fin) {
	kv := t.kv()
	kv["offset"] = fin.Offset
	kv["error"] = fin.Error
	llog.Debug("received fin", kv)
	// we received a fin so don't respond with anything and cleanup
	// transaction
	cleanTx(transactions, t.id)
}
<commit_msg>Added absOffset for logging<commit_after>package transaction

import (
	"math"
	"time"

	"github.com/levenlabs/go-llog"
	"github.com/levenlabs/saturn/config"
	lproto "github.com/levenlabs/saturn/proto"
)

// IncomingMessage takes a message just received from the remote address and
// processes it. It may return a message which should be sent back to the
// remote, or it may return nil. It also returns bool signaling to end
// the transaction if true.
func IncomingMessage(msg *lproto.TxMsg) (*lproto.TxMsg, bool) {
	im := incomingMsg{
		msg:     msg,
		replyCh: make(chan incomingReply),
	}
	incomingMsgCh <- im
	r := <-im.replyCh
	return r.nextMsg, r.end
}

type incomingMsg struct {
	msg     *lproto.TxMsg
	replyCh chan incomingReply
}

type incomingReply struct {
	nextMsg *lproto.TxMsg
	end     bool
}

var incomingMsgCh = make(chan incomingMsg)

// returns the next message, if any, and a bool indiciating if the transaction
// should be considered ended or not
func incoming(transactions map[string]*tx, msg *lproto.TxMsg) (*lproto.TxMsg, bool) {

	kv := llog.KV{
		"msg": msg,
	}
	if !msg.Valid() {
		llog.Warn("received invalid message (possibly from a newer version?)", kv)
		return nil, false
	}
	if !msg.Verify() {
		llog.Warn("received incorrectly signed message", kv)
		return nil, false
	}

	t := transactions[msg.Id]
	if t != nil && t.expectedSeq != msg.Seq {
		kv["txExpectedSeq"] = t.expectedSeq
		kv["txReceivedSeq"] = msg.Seq
		llog.Warn("received message with wrong seq", kv)
		// end the transaction since we got out of order
		return nil, true
	}

	nextSeq := msg.Seq + 1
	retTx := &lproto.TxMsg{
		Id:  msg.Id,
		Seq: nextSeq,
	}

	var hasInner bool
	var ended bool
	if r := msg.GetInitialReport(); r != nil {
		retTx.Inner = handleIncomingInitialReport(transactions, msg.Id, r)
		hasInner = true

	} else if r := msg.GetReport(); r != nil {
		if t == nil {
			llog.Warn("received Report for unknown transaction", kv)
			// we don't know about this transaction anymore so end
			return nil, true
		}
		iR, iF := handleIncomingReport(t, r)
		if iR != nil {
			retTx.Inner = iR
		} else {
			retTx.Inner = iF
			// since this is a fin, we can destroy this transaction after
			// writing
			ended = true
			defer func() {
				cleanTx(transactions, t.id)
			}()
		}
		hasInner = true

	} else if f := msg.GetFin(); f != nil {
		if t == nil {
			llog.Warn("received Fin for unknown transaction", kv)
			// we don't know about this transaction anymore so end
			return nil, true
		}
		handleFin(transactions, t, f)
		ended = true

	} else {
		llog.Warn("received unknown proto.Message type", kv)
	}

	if hasInner {
		// We re-get t because the transaction might not have existed when t was
		// created, although it definitely exists now
		t = transactions[msg.Id]
		// Add 2 here since the next seq should be the next incoming one which is
		// one greater than the one we just sent
		t.expectedSeq = nextSeq + 1
		t.lastMessage = time.Now()

		retTx.Sign()
	} else {
		retTx = nil
	}
	return retTx, ended
}

// This will only occur on the master's side, the slave will only ever be
// sending an initial report, never receiving one
func handleIncomingInitialReport(transactions map[string]*tx, id string, rep *lproto.InitialReport) *lproto.TxMsg_Report {
	t := newTx(transactions, id, rep.Name)

	now := time.Now()
	diff := time.Duration(rep.Time - now.UnixNano())
	kv := t.kv()
	llog.Debug("incoming initial report", kv, llog.KV{"isMaster": config.IsMaster})

	//store the first offset and we'll calculate the trip time when we get the
	//first report
	t.offsets = append(t.offsets, diff)

	return &lproto.TxMsg_Report{
		Report: &lproto.Report{
			Diff: int64(diff),
			Time: now.UnixNano(),
		},
	}
}

func handleIncomingReport(t *tx, rep *lproto.Report) (*lproto.TxMsg_Report, *lproto.TxMsg_Fin) {
	now := time.Now()
	diff := time.Duration(rep.Time - now.UnixNano())
	kv := t.kv()
	llog.Debug("incoming report", kv, llog.KV{"isMaster": config.IsMaster})

	if config.IsMaster {
		//if this is the 3rd packet then we need to include the RTT for the first offest
		if len(t.offsets) == 1 && len(t.tripTimes) == 0 {
			t.tripTimes = append(t.tripTimes, time.Duration(rep.Diff)-t.offsets[0])
		}
		t.tripTimes = append(t.tripTimes, now.Sub(t.lastMessage))
		t.offsets = append(t.offsets, diff)
		kv["txNumTrips"] = len(t.tripTimes)

		//first trip is free and then each iteration is 2 trips
		//seq starts at 1 so after 1 iteration it'll be at 3
		//only the master can terminate a sequence
		if (t.expectedSeq / 2) >= config.Iterations {
			offset, err := calculateAverageOffset(t.tripTimes, t.offsets)
			fin := &lproto.Fin{}
			if err != nil {
				fin.Error = err.Error()
				llog.Error("error calculating avg offset", kv.Set("err", err))
			} else {
				fin.Offset = offset
				kv["offset"] = offset
				absOff := math.Abs(offset)
				llog.Info("slave offset", kv, llog.KV{"absOffset": absOff})
				if config.Threshold < absOff {
					llog.Warn("slave offset is over threshold", kv)
				}
			}
			llog.Debug("over iterations, ending transaction", kv)
			return nil, &lproto.TxMsg_Fin{
				Fin: fin,
			}
		}
	}

	return &lproto.TxMsg_Report{
		Report: &lproto.Report{
			Diff: int64(diff),
			Time: now.UnixNano(),
		},
	}, nil
}

func handleFin(transactions map[string]*tx, t *tx, fin *lproto.Fin) {
	kv := t.kv()
	kv["offset"] = fin.Offset
	kv["error"] = fin.Error
	llog.Debug("received fin", kv)
	// we received a fin so don't respond with anything and cleanup
	// transaction
	cleanTx(transactions, t.id)
}
<|endoftext|>


<commit_before>package transfer

import (
	"fmt"
	"sync"
	"time"

	"github.com/github/git-lfs/errutil"
	"github.com/rubyist/tracerx"
)

const (
	// objectExpirationGracePeriod is the grace period applied to objects
	// when checking whether or not they have expired.
	objectExpirationGracePeriod = 5 * time.Second
)

// adapterBase implements the common functionality for core adapters which
// process transfers with N workers handling an oid each, and which wait for
// authentication to succeed on one worker before proceeding
type adapterBase struct {
	name         string
	direction    Direction
	transferImpl transferImplementation
	jobChan      chan *Transfer
	cb           TransferProgressCallback
	outChan      chan TransferResult
	// WaitGroup to sync the completion of all workers
	workerWait sync.WaitGroup
	// WaitGroup to serialise the first transfer response to perform login if needed
	authWait sync.WaitGroup
}

// transferImplementation must be implemented to provide the actual upload/download
// implementation for all core transfer approaches that use adapterBase for
// convenience. This function will be called on multiple goroutines so it
// must be either stateless or thread safe. However it will never be called
// for the same oid in parallel.
// If authOkFunc is not nil, implementations must call it as early as possible
// when authentication succeeded, before the whole file content is transferred
type transferImplementation interface {
	// WorkerStarting is called when a worker goroutine starts to process jobs
	// Implementations can run some startup logic here & return some context if needed
	WorkerStarting(workerNum int) (interface{}, error)
	// WorkerEnding is called when a worker goroutine is shutting down
	// Implementations can clean up per-worker resources here, context is as returned from WorkerStarted
	WorkerEnding(workerNum int, ctx interface{})
	// DoTransfer performs a single transfer within a worker. ctx is any context returned from WorkerStarted
	DoTransfer(ctx interface{}, t *Transfer, cb TransferProgressCallback, authOkFunc func()) error
}

func newAdapterBase(name string, dir Direction, ti transferImplementation) *adapterBase {
	return &adapterBase{name: name, direction: dir, transferImpl: ti}
}

func (a *adapterBase) Name() string {
	return a.name
}

func (a *adapterBase) Direction() Direction {
	return a.direction
}

func (a *adapterBase) Begin(maxConcurrency int, cb TransferProgressCallback, completion chan TransferResult) error {
	a.cb = cb
	a.outChan = completion
	a.jobChan = make(chan *Transfer, 100)

	tracerx.Printf("xfer: adapter %q Begin() with %d workers", a.Name(), maxConcurrency)

	a.workerWait.Add(maxConcurrency)
	a.authWait.Add(1)
	for i := 0; i < maxConcurrency; i++ {
		ctx, err := a.transferImpl.WorkerStarting(i)
		if err != nil {
			return err
		}
		go a.worker(i, ctx)
	}
	tracerx.Printf("xfer: adapter %q started", a.Name())
	return nil
}

func (a *adapterBase) Add(t *Transfer) {
	tracerx.Printf("xfer: adapter %q Add() for %q", a.Name(), t.Object.Oid)
	a.jobChan <- t
}

func (a *adapterBase) End() {
	tracerx.Printf("xfer: adapter %q End()", a.Name())
	close(a.jobChan)
	// wait for all transfers to complete
	a.workerWait.Wait()
	if a.outChan != nil {
		close(a.outChan)
	}
	tracerx.Printf("xfer: adapter %q stopped", a.Name())
}

// worker function, many of these run per adapter
func (a *adapterBase) worker(workerNum int, ctx interface{}) {

	tracerx.Printf("xfer: adapter %q worker %d starting", a.Name(), workerNum)
	waitForAuth := workerNum > 0
	signalAuthOnResponse := workerNum == 0

	// First worker is the only one allowed to start immediately
	// The rest wait until successful response from 1st worker to
	// make sure only 1 login prompt is presented if necessary
	// Deliberately outside jobChan processing so we know worker 0 will process 1st item
	if waitForAuth {
		tracerx.Printf("xfer: adapter %q worker %d waiting for Auth", a.Name(), workerNum)
		a.authWait.Wait()
		tracerx.Printf("xfer: adapter %q worker %d auth signal received", a.Name(), workerNum)
	}

	for t := range a.jobChan {
		var authCallback func()
		if signalAuthOnResponse {
			authCallback = func() {
				a.authWait.Done()
				signalAuthOnResponse = false
			}
		}
		tracerx.Printf("xfer: adapter %q worker %d processing job for %q", a.Name(), workerNum, t.Object.Oid)

		// Actual transfer happens here
		var err error
		if t.Object.IsExpired(time.Now().Add(objectExpirationGracePeriod)) {
			tracerx.Printf("xfer: adapter %q worker %d found job for %q expired, retrying...", a.Name(), workerNum, t.Object.Oid)
			err = errutil.NewRetriableError(fmt.Errorf("lfs/transfer: object %q has expired", t.Object.Oid))
		} else {
			err = a.transferImpl.DoTransfer(ctx, t, a.cb, authCallback)
		}

		if a.outChan != nil {
			res := TransferResult{t, err}
			a.outChan <- res
		}

		tracerx.Printf("xfer: adapter %q worker %d finished job for %q", a.Name(), workerNum, t.Object.Oid)
	}
	// This will only happen if no jobs were submitted; just wake up all workers to finish
	if signalAuthOnResponse {
		a.authWait.Done()
	}
	tracerx.Printf("xfer: adapter %q worker %d stopping", a.Name(), workerNum)
	a.transferImpl.WorkerEnding(workerNum, ctx)
	a.workerWait.Done()
}

func advanceCallbackProgress(cb TransferProgressCallback, t *Transfer, numBytes int64) {
	if cb != nil {
		// Must split into max int sizes since read count is int
		const maxInt = int(^uint(0) >> 1)
		for read := int64(0); read < numBytes; {
			remainder := numBytes - read
			if remainder > int64(maxInt) {
				read += int64(maxInt)
				cb(t.Name, t.Object.Size, read, maxInt)
			} else {
				read += remainder
				cb(t.Name, t.Object.Size, read, int(remainder))
			}

		}
	}
}
<commit_msg>Comment fixes<commit_after>package transfer

import (
	"fmt"
	"sync"
	"time"

	"github.com/github/git-lfs/errutil"
	"github.com/rubyist/tracerx"
)

const (
	// objectExpirationGracePeriod is the grace period applied to objects
	// when checking whether or not they have expired.
	objectExpirationGracePeriod = 5 * time.Second
)

// adapterBase implements the common functionality for core adapters which
// process transfers with N workers handling an oid each, and which wait for
// authentication to succeed on one worker before proceeding
type adapterBase struct {
	name         string
	direction    Direction
	transferImpl transferImplementation
	jobChan      chan *Transfer
	cb           TransferProgressCallback
	outChan      chan TransferResult
	// WaitGroup to sync the completion of all workers
	workerWait sync.WaitGroup
	// WaitGroup to serialise the first transfer response to perform login if needed
	authWait sync.WaitGroup
}

// transferImplementation must be implemented to provide the actual upload/download
// implementation for all core transfer approaches that use adapterBase for
// convenience. This function will be called on multiple goroutines so it
// must be either stateless or thread safe. However it will never be called
// for the same oid in parallel.
// If authOkFunc is not nil, implementations must call it as early as possible
// when authentication succeeded, before the whole file content is transferred
type transferImplementation interface {
	// WorkerStarting is called when a worker goroutine starts to process jobs
	// Implementations can run some startup logic here & return some context if needed
	WorkerStarting(workerNum int) (interface{}, error)
	// WorkerEnding is called when a worker goroutine is shutting down
	// Implementations can clean up per-worker resources here, context is as returned from WorkerStarting
	WorkerEnding(workerNum int, ctx interface{})
	// DoTransfer performs a single transfer within a worker. ctx is any context returned from WorkerStarting
	DoTransfer(ctx interface{}, t *Transfer, cb TransferProgressCallback, authOkFunc func()) error
}

func newAdapterBase(name string, dir Direction, ti transferImplementation) *adapterBase {
	return &adapterBase{name: name, direction: dir, transferImpl: ti}
}

func (a *adapterBase) Name() string {
	return a.name
}

func (a *adapterBase) Direction() Direction {
	return a.direction
}

func (a *adapterBase) Begin(maxConcurrency int, cb TransferProgressCallback, completion chan TransferResult) error {
	a.cb = cb
	a.outChan = completion
	a.jobChan = make(chan *Transfer, 100)

	tracerx.Printf("xfer: adapter %q Begin() with %d workers", a.Name(), maxConcurrency)

	a.workerWait.Add(maxConcurrency)
	a.authWait.Add(1)
	for i := 0; i < maxConcurrency; i++ {
		ctx, err := a.transferImpl.WorkerStarting(i)
		if err != nil {
			return err
		}
		go a.worker(i, ctx)
	}
	tracerx.Printf("xfer: adapter %q started", a.Name())
	return nil
}

func (a *adapterBase) Add(t *Transfer) {
	tracerx.Printf("xfer: adapter %q Add() for %q", a.Name(), t.Object.Oid)
	a.jobChan <- t
}

func (a *adapterBase) End() {
	tracerx.Printf("xfer: adapter %q End()", a.Name())
	close(a.jobChan)
	// wait for all transfers to complete
	a.workerWait.Wait()
	if a.outChan != nil {
		close(a.outChan)
	}
	tracerx.Printf("xfer: adapter %q stopped", a.Name())
}

// worker function, many of these run per adapter
func (a *adapterBase) worker(workerNum int, ctx interface{}) {

	tracerx.Printf("xfer: adapter %q worker %d starting", a.Name(), workerNum)
	waitForAuth := workerNum > 0
	signalAuthOnResponse := workerNum == 0

	// First worker is the only one allowed to start immediately
	// The rest wait until successful response from 1st worker to
	// make sure only 1 login prompt is presented if necessary
	// Deliberately outside jobChan processing so we know worker 0 will process 1st item
	if waitForAuth {
		tracerx.Printf("xfer: adapter %q worker %d waiting for Auth", a.Name(), workerNum)
		a.authWait.Wait()
		tracerx.Printf("xfer: adapter %q worker %d auth signal received", a.Name(), workerNum)
	}

	for t := range a.jobChan {
		var authCallback func()
		if signalAuthOnResponse {
			authCallback = func() {
				a.authWait.Done()
				signalAuthOnResponse = false
			}
		}
		tracerx.Printf("xfer: adapter %q worker %d processing job for %q", a.Name(), workerNum, t.Object.Oid)

		// Actual transfer happens here
		var err error
		if t.Object.IsExpired(time.Now().Add(objectExpirationGracePeriod)) {
			tracerx.Printf("xfer: adapter %q worker %d found job for %q expired, retrying...", a.Name(), workerNum, t.Object.Oid)
			err = errutil.NewRetriableError(fmt.Errorf("lfs/transfer: object %q has expired", t.Object.Oid))
		} else {
			err = a.transferImpl.DoTransfer(ctx, t, a.cb, authCallback)
		}

		if a.outChan != nil {
			res := TransferResult{t, err}
			a.outChan <- res
		}

		tracerx.Printf("xfer: adapter %q worker %d finished job for %q", a.Name(), workerNum, t.Object.Oid)
	}
	// This will only happen if no jobs were submitted; just wake up all workers to finish
	if signalAuthOnResponse {
		a.authWait.Done()
	}
	tracerx.Printf("xfer: adapter %q worker %d stopping", a.Name(), workerNum)
	a.transferImpl.WorkerEnding(workerNum, ctx)
	a.workerWait.Done()
}

func advanceCallbackProgress(cb TransferProgressCallback, t *Transfer, numBytes int64) {
	if cb != nil {
		// Must split into max int sizes since read count is int
		const maxInt = int(^uint(0) >> 1)
		for read := int64(0); read < numBytes; {
			remainder := numBytes - read
			if remainder > int64(maxInt) {
				read += int64(maxInt)
				cb(t.Name, t.Object.Size, read, maxInt)
			} else {
				read += remainder
				cb(t.Name, t.Object.Size, read, int(remainder))
			}

		}
	}
}
<|endoftext|>


<commit_before>/*
Package timing provides a plugin Xavi wires in as the enclosing wrapper for the user specified
plugin chain. This plugin creates and puts an EndToEndTimer into the context that downstream
components may annotate with the service name and contributors of note. The JSON representation
of the timing is logged on completion of the wrapped call chain.
*/
package timing

import (
	"expvar"
	"fmt"
	"github.com/armon/go-metrics"
	"github.com/xtracdev/xavi/plugin"
	_ "github.com/xtracdev/xavi/statsd"
	"github.com/xtracdev/xavi/timer"
	"golang.org/x/net/context"
	"net/http"
	"os"
	"strings"
	"time"
	"unicode"
)

type key int

const timerKey key = -22132
const serviceNameKey key = -22133

var counts = expvar.NewMap("counters")

//NewContextWithTimer adds a new timer to the request context
func NewContextWithTimer(ctx context.Context, req *http.Request) context.Context {
	timer := timer.NewEndToEndTimer("unspecified timer")
	return context.WithValue(ctx, timerKey, timer)
}

//AddServiceNameToContext adds the name of the service the backend handler will invoke. This provides
//a service name in the output timing log to allow the latency of different backend services to be
//assessed.
func AddServiceNameToContext(ctx context.Context, serviceName string) context.Context {
	return context.WithValue(ctx, serviceNameKey, serviceName)
}

//GetServiceNameFromContext pulls the service name from the context.
func GetServiceNameFromContext(ctx context.Context) string {
	serviceName, ok := ctx.Value(serviceNameKey).(string)
	if !ok {
		return ""
	}

	return serviceName
}

//TimerFromContext returns an EndToEndTimer from the given context if one
//is present, otherwise nil is returned
func TimerFromContext(ctx context.Context) *timer.EndToEndTimer {
	newCtx, ok := ctx.Value(timerKey).(*timer.EndToEndTimer)
	if !ok {
		return nil
	}

	return newCtx
}

type TimingWrapper struct{}

func NewTimingWrapper() TimingWrapper {
	return TimingWrapper{}
}

//Wrap implements the plugin Wrapper interface, and is used
//to wrap a handler to put a EndToEndTimer instance into the call context
func (tw TimingWrapper) Wrap(h plugin.ContextHandler) plugin.ContextHandler {
	return plugin.ContextHandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
		ctx = NewContextWithTimer(ctx, req)
		h.ServeHTTPContext(ctx, rw, req)
		ctxTimer := TimerFromContext(ctx)
		ctxTimer.Stop(nil)
		go func(t *timer.EndToEndTimer) {
			logTiming(t)
		}(ctxTimer)
	})
}

//Function to log timing data for later analysis
func logTiming(t *timer.EndToEndTimer) {
	//We add a timestamp to the JSON to allow indexing in elasticsearch
	t.LoggingTimestamp = time.Now()

	fmt.Fprintln(os.Stderr, t.ToJSONString())

	go func(t *timer.EndToEndTimer) {
		updateCounters(t)
	}(t)
}

//Function to modify epvar counters
func updateCounters(t *timer.EndToEndTimer) {
	if t.ErrorFree {
		countName := spaceMap(t.Name + "-count")
		counts.Add(countName, 1)
		metrics.IncrCounter([]string{countName}, 1.0)
		writeTimingsToStatsd(t)
	} else {
		counts.Add(t.Name+"-errors", 1)
	}
}

func spaceMap(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

//Send timing data to statsd
func writeTimingsToStatsd(t *timer.EndToEndTimer) {
	metrics.AddSample([]string{spaceMap(t.Name)}, float32(t.Duration))
	for _, c := range t.Contributors {
		metrics.AddSample([]string{spaceMap(t.Name + "." + c.Name)}, float32(c.Duration))
		for _, sc := range c.ServiceCalls {
			metrics.AddSample([]string{spaceMap(t.Name + "." + c.Name + "." + sc.Name)}, float32(sc.Duration))
		}
	}
}
<commit_msg>Fixed return type of NewTimingWrapper<commit_after>/*
Package timing provides a plugin Xavi wires in as the enclosing wrapper for the user specified
plugin chain. This plugin creates and puts an EndToEndTimer into the context that downstream
components may annotate with the service name and contributors of note. The JSON representation
of the timing is logged on completion of the wrapped call chain.
*/
package timing

import (
	"expvar"
	"fmt"
	"github.com/armon/go-metrics"
	"github.com/xtracdev/xavi/plugin"
	_ "github.com/xtracdev/xavi/statsd"
	"github.com/xtracdev/xavi/timer"
	"golang.org/x/net/context"
	"net/http"
	"os"
	"strings"
	"time"
	"unicode"
)

type key int

const timerKey key = -22132
const serviceNameKey key = -22133

var counts = expvar.NewMap("counters")

//NewContextWithTimer adds a new timer to the request context
func NewContextWithTimer(ctx context.Context, req *http.Request) context.Context {
	timer := timer.NewEndToEndTimer("unspecified timer")
	return context.WithValue(ctx, timerKey, timer)
}

//AddServiceNameToContext adds the name of the service the backend handler will invoke. This provides
//a service name in the output timing log to allow the latency of different backend services to be
//assessed.
func AddServiceNameToContext(ctx context.Context, serviceName string) context.Context {
	return context.WithValue(ctx, serviceNameKey, serviceName)
}

//GetServiceNameFromContext pulls the service name from the context.
func GetServiceNameFromContext(ctx context.Context) string {
	serviceName, ok := ctx.Value(serviceNameKey).(string)
	if !ok {
		return ""
	}

	return serviceName
}

//TimerFromContext returns an EndToEndTimer from the given context if one
//is present, otherwise nil is returned
func TimerFromContext(ctx context.Context) *timer.EndToEndTimer {
	newCtx, ok := ctx.Value(timerKey).(*timer.EndToEndTimer)
	if !ok {
		return nil
	}

	return newCtx
}

type TimingWrapper struct{}

func NewTimingWrapper() plugin.Wrapper {
	return TimingWrapper{}
}

//Wrap implements the plugin Wrapper interface, and is used
//to wrap a handler to put a EndToEndTimer instance into the call context
func (tw TimingWrapper) Wrap(h plugin.ContextHandler) plugin.ContextHandler {
	return plugin.ContextHandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) {
		ctx = NewContextWithTimer(ctx, req)
		h.ServeHTTPContext(ctx, rw, req)
		ctxTimer := TimerFromContext(ctx)
		ctxTimer.Stop(nil)
		go func(t *timer.EndToEndTimer) {
			logTiming(t)
		}(ctxTimer)
	})
}

//Function to log timing data for later analysis
func logTiming(t *timer.EndToEndTimer) {
	//We add a timestamp to the JSON to allow indexing in elasticsearch
	t.LoggingTimestamp = time.Now()

	fmt.Fprintln(os.Stderr, t.ToJSONString())

	go func(t *timer.EndToEndTimer) {
		updateCounters(t)
	}(t)
}

//Function to modify epvar counters
func updateCounters(t *timer.EndToEndTimer) {
	if t.ErrorFree {
		countName := spaceMap(t.Name + "-count")
		counts.Add(countName, 1)
		metrics.IncrCounter([]string{countName}, 1.0)
		writeTimingsToStatsd(t)
	} else {
		counts.Add(t.Name+"-errors", 1)
	}
}

func spaceMap(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

//Send timing data to statsd
func writeTimingsToStatsd(t *timer.EndToEndTimer) {
	metrics.AddSample([]string{spaceMap(t.Name)}, float32(t.Duration))
	for _, c := range t.Contributors {
		metrics.AddSample([]string{spaceMap(t.Name + "." + c.Name)}, float32(c.Duration))
		for _, sc := range c.ServiceCalls {
			metrics.AddSample([]string{spaceMap(t.Name + "." + c.Name + "." + sc.Name)}, float32(sc.Duration))
		}
	}
}
<|endoftext|>


<commit_before>package build

import (
	"fmt"
	"github.com/fatih/color"
	"io/ioutil"
	"log"
	"regexp"
	"strings"

	"github.com/servehub/serve/manifest"
	"github.com/servehub/utils"
)

func init() {
	manifest.PluginRegestry.Add("build.docker-image", BuildDockerImage{})
}

type BuildDockerImage struct{}

func (p BuildDockerImage) Run(data manifest.Manifest) error {
	image := data.GetString("image")

	if data.Has("repository") {
		image = data.GetString("repository") + image[strings.Index(image, "/"):]
	}

	if data.Has("category") {
		image = image[:strings.Index(image, "/")] + "/" + data.GetString("category") + image[strings.LastIndex(image, "/"):]
	}

	if data.Has("name") {
		image = image[:strings.LastIndex(image, "/")] + "/" + data.GetString("name") + image[strings.Index(image, ":"):]
	}

	prefix := image[:strings.Index(image, ":")]

	if data.Has("login.user") {
		if err := utils.RunCmd(
			`docker login -u "%s" -p "%s" %s`,
			data.GetString("login.user"),
			data.GetString("login.password"),
			image[:strings.Index(image, "/")],
		); err != nil {
			return err
		}
	}

	buildArgs := data.GetString("build-args")
	workdir := data.GetString("workdir") + "/"

	if data.Has("dockerfile") {
		buildArgs += " --file " + workdir + data.GetString("dockerfile")
	}

	tags := make([]string, 0)
	for _, tag := range data.GetArrayForce("tags") {
		tags = append(tags, fmt.Sprintf("%s:%v", prefix, tag))
	}

	// pull exists tagged images for cache
	for _, tag := range tags {
		utils.RunCmd("docker pull %s", tag)
	}

	cacheFrom := ""
	if len(tags) == 0 {
		tags = []string{image, fmt.Sprintf("%s:%v", prefix, "latest")}
		cacheFrom = "--cache-from=" + tags[1]
	} else {
		cacheFrom = "--cache-from=" + tags[0]
	}

	if err := utils.RunCmd(
		"docker build %s -t %s %s %s",
		buildArgs,
		strings.Join(tags, " -t "),
		cacheFrom,
		data.GetString("workdir"),
	); err != nil {
		return err
	}

	if !data.GetBool("no-push") {
		for _, tag := range tags {
			if err := utils.RunCmd("docker push %s", tag); err != nil {
				return err
			}
		}
	}

	if data.Has("images") && len(data.GetArray("images")) > 0 {
		for _, image := range data.GetArray("images") {
			if image.Has("branch") && image.GetString("branch") != data.GetStringOr("current-branch", "master") {
				if image.GetString("branch") != "*" {
					if m, _ := regexp.MatchString("^"+image.GetString("branch")+"$", data.GetStringOr("current-branch", "master")); !m {
						continue
					}
				}
			}

			if _, err := ioutil.ReadFile(workdir + image.GetString("dockerfile")); err != nil {
				continue
			}

			for k, v := range data.GetMap("/") {
				if k != "images" && !image.Has(k) {
					image.Set(k, v.Unwrap())
				}
			}

			fmt.Printf("\n")

			log.Printf("%s\n%s\n\n", color.GreenString(">>> build.docker-image sub-image:"), color.CyanString("%s", image.String()))

			if err := p.Run(image); err != nil {
				return err
			}

			fmt.Printf("\n")
		}
	}

	return nil
}
<commit_msg>docker-image: add skip-errors flag<commit_after>package build

import (
	"fmt"
	"github.com/fatih/color"
	"io/ioutil"
	"log"
	"regexp"
	"strings"

	"github.com/servehub/serve/manifest"
	"github.com/servehub/utils"
)

func init() {
	manifest.PluginRegestry.Add("build.docker-image", BuildDockerImage{})
}

type BuildDockerImage struct{}

func (p BuildDockerImage) Run(data manifest.Manifest) error {
	image := data.GetString("image")

	if data.Has("repository") {
		image = data.GetString("repository") + image[strings.Index(image, "/"):]
	}

	if data.Has("category") {
		image = image[:strings.Index(image, "/")] + "/" + data.GetString("category") + image[strings.LastIndex(image, "/"):]
	}

	if data.Has("name") {
		image = image[:strings.LastIndex(image, "/")] + "/" + data.GetString("name") + image[strings.Index(image, ":"):]
	}

	prefix := image[:strings.Index(image, ":")]

	if data.Has("login.user") {
		if err := utils.RunCmd(
			`docker login -u "%s" -p "%s" %s`,
			data.GetString("login.user"),
			data.GetString("login.password"),
			image[:strings.Index(image, "/")],
		); err != nil {
			return err
		}
	}

	buildArgs := data.GetString("build-args")
	workdir := data.GetString("workdir") + "/"

	if data.Has("dockerfile") {
		buildArgs += " --file " + workdir + data.GetString("dockerfile")
	}

	tags := make([]string, 0)
	for _, tag := range data.GetArrayForce("tags") {
		tags = append(tags, fmt.Sprintf("%s:%v", prefix, tag))
	}

	// pull exists tagged images for cache
	for _, tag := range tags {
		utils.RunCmd("docker pull %s", tag)
	}

	cacheFrom := ""
	if len(tags) == 0 {
		tags = []string{image, fmt.Sprintf("%s:%v", prefix, "latest")}
		cacheFrom = "--cache-from=" + tags[1]
	} else {
		cacheFrom = "--cache-from=" + tags[0]
	}

	if err := utils.RunCmd(
		"docker build %s -t %s %s %s",
		buildArgs,
		strings.Join(tags, " -t "),
		cacheFrom,
		data.GetString("workdir"),
	); err != nil {
		return err
	}

	if !data.GetBool("no-push") {
		for _, tag := range tags {
			if err := utils.RunCmd("docker push %s", tag); err != nil {
				return err
			}
		}
	}

	if data.Has("images") && len(data.GetArray("images")) > 0 {
		for _, image := range data.GetArray("images") {
			if image.Has("branch") && image.GetString("branch") != data.GetStringOr("current-branch", "master") {
				if image.GetString("branch") != "*" {
					if m, _ := regexp.MatchString("^"+image.GetString("branch")+"$", data.GetStringOr("current-branch", "master")); !m {
						continue
					}
				}
			}

			if _, err := ioutil.ReadFile(workdir + image.GetString("dockerfile")); err != nil {
				continue
			}

			for k, v := range data.GetMap("/") {
				if k != "images" && !image.Has(k) {
					image.Set(k, v.Unwrap())
				}
			}

			fmt.Printf("\n")

			log.Printf("%s\n%s\n\n", color.GreenString(">>> build.docker-image sub-image:"), color.CyanString("%s", image.String()))

			if err := p.Run(image); err != nil {
				if image.GetBool("skip-errors") {
					log.Printf("Error on build sub-image: %v", err)
				} else {
					return err
				}
			}

			fmt.Printf("\n")
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// +build go1.8

package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/docopt/docopt-go"
	"github.com/tisp-lang/tisp/src/lib/compile"
	"github.com/tisp-lang/tisp/src/lib/run"
	"github.com/tisp-lang/tisp/src/lib/util"
)

func main() {
	defer func() {
		switch x := recover().(type) {
		case error:
			printToStderr(x.Error())
		case string:
			printToStderr(x)
		default:
			if x != nil {
				panic(x)
			}
		}
	}()

	run.Run(compile.MainModule(getArgs()["<filename>"].(string)))
}

func getArgs() map[string]interface{} {
	usage := `Tisp interpreter

Usage:
  tisp [<filename>]

Options:
  -h, --help  Show this help.`

	args, err := docopt.Parse(usage, nil, true, "Tisp 0.0.0", false)

	if err != nil {
		util.PanicError(err)
	} else if args["<filename>"] == nil {
		args["<filename>"] = ""
	}

	return args
}

func printToStderr(s string) {
	fmt.Fprintf(os.Stderr, strings.TrimSpace(s)+"\n")
}
<commit_msg>Don't use util package in main.go<commit_after>// +build go1.8

package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/docopt/docopt-go"
	"github.com/tisp-lang/tisp/src/lib/compile"
	"github.com/tisp-lang/tisp/src/lib/run"
)

func main() {
	defer func() {
		switch x := recover().(type) {
		case error:
			printToStderr(x.Error())
		case string:
			printToStderr(x)
		default:
			if x != nil {
				panic(x)
			}
		}
	}()

	run.Run(compile.MainModule(getArgs()["<filename>"].(string)))
}

func getArgs() map[string]interface{} {
	usage := `Tisp interpreter

Usage:
  tisp [<filename>]

Options:
  -h, --help  Show this help.`

	args, err := docopt.Parse(usage, nil, true, "Tisp 0.0.0", false)

	if err != nil {
		printToStderr(err.Error())
		os.Exit(1)
	} else if args["<filename>"] == nil {
		args["<filename>"] = ""
	}

	return args
}

func printToStderr(s string) {
	fmt.Fprintf(os.Stderr, strings.TrimSpace(s)+"\n")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"regexp"
)

var fileRoute = regexp.MustCompile("^/files/([^/]+)$")

func serveHttp() error {
	http.HandleFunc("/", route)

	addr := ":1080"
	log.Printf("serving clients at %s", addr)

	return http.ListenAndServe(addr, nil)
}

func route(w http.ResponseWriter, r *http.Request) {
	log.Printf("request: %s %s", r.Method, r.URL.RequestURI())

	w.Header().Set("Server", "tusd")

	if r.Method == "POST" && r.URL.Path == "/files" {
		postFiles(w, r)
	} else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil {
		id := match[1]
		// WIP
		switch r.Method {
		case "HEAD":
			headFile(w, r, id)
		case "GET":
			reply(w, http.StatusNotImplemented, "File download")
		case "PUT":
			putFile(w, r, id)
		default:
			reply(w, http.StatusMethodNotAllowed, "Invalid http method")
		}
	} else {
		reply(w, http.StatusNotFound, "No matching route")
	}
}

func reply(w http.ResponseWriter, code int, message string) {
	w.WriteHeader(code)
	fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message)
}

func postFiles(w http.ResponseWriter, r *http.Request) {
	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		reply(w, http.StatusBadRequest, err.Error())
		return
	}

	if contentRange.Size == -1 {
		reply(w, http.StatusBadRequest, "Content-Range must indicate total file size.")
		return
	}

	if contentRange.End != -1 {
		reply(w, http.StatusNotImplemented, "File data in initial request.")
		return
	}

	contentType := r.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	id := uid()
	if err := initFile(id, contentRange.Size, contentType); err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	// @TODO: Return X-Missing header

	w.Header().Set("Location", "/files/"+id)
	w.WriteHeader(http.StatusCreated)
}

func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		reply(w, http.StatusBadRequest, err.Error())
		return
	}

	// @TODO: Check that file exists
	// @TODO: Make sure contentRange.Size matches file size

	if err := putFileChunk(fileId, contentRange.Start, contentRange.End, r.Body); err != nil {
		// @TODO: Could be a 404 as well
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	// @TODO: Return X-Missing header
}

func headFile(w http.ResponseWriter, r *http.Request, fileId string) {
	chunks, err := getReceivedChunks(fileId)
	if err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	received := ""
	for i, chunk := range chunks {
		received += fmt.Sprintf("%d-%d", chunk.Start, chunk.End)
		if i + 1 < len(chunks) {
			received += ","
		}
	}

	if received != "" {
		w.Header().Set("Range", "bytes="+received)
	}
}
<commit_msg>Return range header for puts<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"regexp"
)

var fileRoute = regexp.MustCompile("^/files/([^/]+)$")

func serveHttp() error {
	http.HandleFunc("/", route)

	addr := ":1080"
	log.Printf("serving clients at %s", addr)

	return http.ListenAndServe(addr, nil)
}

func route(w http.ResponseWriter, r *http.Request) {
	log.Printf("request: %s %s", r.Method, r.URL.RequestURI())

	w.Header().Set("Server", "tusd")

	if r.Method == "POST" && r.URL.Path == "/files" {
		postFiles(w, r)
	} else if match := fileRoute.FindStringSubmatch(r.URL.Path); match != nil {
		id := match[1]
		// WIP
		switch r.Method {
		case "HEAD":
			headFile(w, r, id)
		case "GET":
			reply(w, http.StatusNotImplemented, "File download")
		case "PUT":
			putFile(w, r, id)
		default:
			reply(w, http.StatusMethodNotAllowed, "Invalid http method")
		}
	} else {
		reply(w, http.StatusNotFound, "No matching route")
	}
}

func reply(w http.ResponseWriter, code int, message string) {
	w.WriteHeader(code)
	fmt.Fprintf(w, "%d - %s: %s\n", code, http.StatusText(code), message)
}

func postFiles(w http.ResponseWriter, r *http.Request) {
	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		reply(w, http.StatusBadRequest, err.Error())
		return
	}

	if contentRange.Size == -1 {
		reply(w, http.StatusBadRequest, "Content-Range must indicate total file size.")
		return
	}

	if contentRange.End != -1 {
		reply(w, http.StatusNotImplemented, "File data in initial request.")
		return
	}

	contentType := r.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "application/octet-stream"
	}

	id := uid()
	if err := initFile(id, contentRange.Size, contentType); err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	// @TODO: Return X-Missing header

	w.Header().Set("Location", "/files/"+id)
	w.WriteHeader(http.StatusCreated)
}

func putFile(w http.ResponseWriter, r *http.Request, fileId string) {
	contentRange, err := parseContentRange(r.Header.Get("Content-Range"))
	if err != nil {
		reply(w, http.StatusBadRequest, err.Error())
		return
	}

	// @TODO: Check that file exists
	// @TODO: Make sure contentRange.Size matches file size

	if err := putFileChunk(fileId, contentRange.Start, contentRange.End, r.Body); err != nil {
		// @TODO: Could be a 404 as well
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	setFileRangeHeader(w, fileId)
}

func headFile(w http.ResponseWriter, r *http.Request, fileId string) {
	setFileRangeHeader(w, fileId)
}

func setFileRangeHeader(w http.ResponseWriter, fileId string) {
	chunks, err := getReceivedChunks(fileId)
	if err != nil {
		reply(w, http.StatusInternalServerError, err.Error())
		return
	}

	received := ""
	for i, chunk := range chunks {
		received += fmt.Sprintf("%d-%d", chunk.Start, chunk.End)
		if i + 1 < len(chunks) {
			received += ","
		}
	}

	if received != "" {
		w.Header().Set("Range", "bytes="+received)
	}
}
<|endoftext|>