Skip to content

Commit

Permalink
Merge branch 'release'
Browse files Browse the repository at this point in the history
Release of v1.1

Changes:
- lazy connection to ssh
- fork tview and modify textview to keep only 300kb of data in the
  buffer
- WIP for docker logs
  • Loading branch information
Cosmin Tupangiu committed Feb 25, 2020
2 parents aa1e330 + 23ee48b commit 688eefb
Show file tree
Hide file tree
Showing 16 changed files with 385 additions and 23 deletions.
10 changes: 4 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,21 @@ BIN_FOLDER=bin
.DEFAULT_GOAL: $(TARGET)

# These will be provided to the target
VERSION := 1.0.0
VERSION := 1.1.0
BUILD := `git rev-parse HEAD`
DATE := `date +"%d.%B.%Y-%T"`

# Use linker flags to provide version/build settings to the target
LDFLAGS=-ldflags "-X=main.Version=$(VERSION) -X=main.Build=$(BUILD)"
LDFLAGS=-ldflags "-X=main.Version=$(VERSION) -X=main.Build=$(BUILD) -X=main.BuildDate="$(DATE)""

# go source files, ignore vendor directory
SRC := main.go

# erase swp file
ERASE_SWP := $(shell find . -type f -name '*.swp' | xargs rm)

.PHONY: all build clean install uninstall fmt simplify check run

all: check install

build:$(ERASE_SWP)
build:
@go build -o $(BIN_FOLDER)/$(TARGET) ${LDFLAGS} $(SRC)

clean:
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# LazyLogger

Lazylogger is a small app to watch logs from different hosts in one place. Using the TUI, it is very easy to switch between log files.
Lazylogger is a small app to watch log files from different hosts in one place. Using the TUI, it is very easy to switch between log files.
You can even split, horizontaly or verticaly, the current window and add more logs on the same page.

![Gif](/docs/demo.gif)
Expand Down Expand Up @@ -31,13 +31,17 @@ services:
key: /home/foo/.aws/key.pem
file: /home/ec2-user/apache-tomcat-9.0.30/logs/catalina.out
```
Each entry in `services` represent a log service. You can use password or key to connect to ssh.
Each entry in `services` represent a log service.

Credentials for ssh are set in `host` node. You can use password or key to connect to ssh.
> Lazylogger will connect to port 22 only.

For cases when a jump host is required (e.g. `aws`), you can add a `jumpHost` with the same structre as `host`.
Lazylogger will create a ssh tunnel to connect to `host` thought `jumpHost`.


To use a configuration file, the following command must be executed:

`lazylogger --config config.yml`

>Lazylogger will try to connect only when a new logger is created.
> Lazylogger will try to connect only when a new logger is created.

7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ module github.com/tupyy/lazylogger
go 1.13

require (
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v1.13.1
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/gdamore/tcell v1.3.0
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/helloyi/go-sshclient v0.0.0-20191203124208-f1e205501005
github.com/mitchellh/mapstructure v1.1.2
github.com/rivo/tview v0.0.0-20191229165609-1ee8d9874dcf
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
github.com/spf13/viper v1.6.1
github.com/tupyy/tview v0.0.0-20200224131948-26c249413419
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
)
24 changes: 22 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
Expand Down Expand Up @@ -60,17 +68,24 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand All @@ -83,8 +98,6 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/tview v0.0.0-20191229165609-1ee8d9874dcf h1:rh73WIukDlFIRqk1lk76or+LExEjTci2789EDvDD67U=
github.com/rivo/tview v0.0.0-20191229165609-1ee8d9874dcf/go.mod h1:/rBeY22VG2QprWnEqG57IBC8biVu3i0DOIjRLc9I8H0=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
Expand All @@ -109,6 +122,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tupyy/tview v0.0.0-20200224131948-26c249413419 h1:jC5duhmqrVQrsKBBPnAJEiKueQXU+TdJyWoPa15AoyE=
github.com/tupyy/tview v0.0.0-20200224131948-26c249413419/go.mod h1:+qO6bWtbR++NlYTTskv9ckSKsy95jt39J5EBGjDEIlw=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
Expand All @@ -127,6 +142,7 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -138,9 +154,13 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ=
golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
Expand Down
78 changes: 78 additions & 0 deletions internal/docker/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package docker

import (
"bytes"
"context"
"io"
"log"
"net/http"
"time"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
)

// DockerReader reads docker containers' logs.
// It implements the FileReader interface.
type DockerReader struct {

// docker client
client *client.Client

// id of the container which logs are read
containerId string
}

func NewDockerLogReader(host, version string) (*DockerReader, error) {
var defaultTtransport http.RoundTripper = &http.Transport{Proxy: nil}
c := &http.Client{Transport: defaultTtransport}

if host == "" {
host = client.DefaultDockerHost
}

if version == "" {
version = client.DefaultVersion
}

cli, err := client.NewClient(host, version, c, nil)
if err != nil {
return &DockerReader{}, err
}

return &DockerReader{client: cli}, nil
}

// Clone returns a clone of DockerReader.
func (d *DockerReader) Clone() *DockerReader {
var clone = DockerReader{client: d.client}
return &clone
}

//ListContainers is a wrapper around ContainerList with timeout context of 5 seconds.
func (d *DockerReader) ListContainers() ([]types.Container, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

return d.client.ContainerList(ctx, types.ContainerListOptions{})
}

// Reads the logs from containerId and return an array of bytes, number of bytes read and error if any.
func containerLogs(client *client.Client, containerId string) ([]byte, int64, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

reader, err := client.ContainerLogs(ctx, "container_id", types.ContainerLogsOptions{})
if err != nil {
log.Fatal(err)
}

buf := &bytes.Buffer{}
n, err := io.Copy(buf, reader)
if err != nil && err != io.EOF {
return []byte{}, n, err
}

return buf.Bytes(), n, nil

}
2 changes: 1 addition & 1 deletion internal/gui/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"time"

"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/tupyy/tview"
"github.com/tupyy/lazylogger/internal/log"
)

Expand Down
4 changes: 2 additions & 2 deletions internal/gui/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"strings"

"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/tupyy/tview"
)

var helpTextView = tview.NewTextView()

const (
subtitle = `lazylogger v2.0 - Visualize logs from different hosts`
subtitle = `lazylogger v1.1 - Visualize logs from different hosts`
navigation = `Right arrow: Next Page Left arrow: Previous Page P: Show Help Ctrl-C: Exit`
pages = `Ctrl+A: Add page Ctrl+X: Delete Page`
window = `v: Vertical Split h: Hortizontal Split m: Show Menu x: Remove selected view`
Expand Down
2 changes: 1 addition & 1 deletion internal/gui/logmainview.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package gui

import (
"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/tupyy/tview"
"github.com/tupyy/lazylogger/internal/conf"
)

Expand Down
2 changes: 1 addition & 1 deletion internal/gui/logview.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"path"

"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/tupyy/tview"
"github.com/tupyy/lazylogger/internal/conf"
)

Expand Down
2 changes: 1 addition & 1 deletion internal/gui/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"path"

"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/tupyy/tview"
"github.com/tupyy/lazylogger/internal/conf"
)

Expand Down
2 changes: 1 addition & 1 deletion internal/gui/navbar.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package gui
import (
"fmt"

"github.com/rivo/tview"
"github.com/tupyy/tview"
)

// NavBar displays the navigation bar at the bottom of the screen.
Expand Down
93 changes: 93 additions & 0 deletions internal/log/bytesreader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package log

// Docker represents a docker client.
type Docker interface {

// ContainerLogs returns the log of the container as []byte, the size of log.
ContainerLogs(containerId string) ([]byte, int32, error, error)
}

// BytesReader provides an implementation of FileReader interface.
// It works with array of bytes returned by the docker client.
// Due to the fact that we don't know the size of the log before we read it, the behaviour is different then
// RemoteReader. The data is fetched in the FetchSize method and the size of the log is returned.
// Also, we have to keep the difference between the last received data and the actual data. This differece will be return when
// ReadNextChunk is called.
type BytesReader struct {

// container id
id string

// Implementation of Docker interface
client Docker

// holds the last data read from container
data []byte

// offset represents the last read position
offset int32

// total bytes read so far
size int32
}

// NewBytesReader creates a new BytesReader.
func NewBytesReader(id string, client Docker) *BytesReader {
return &BytesReader{id, client, []byte(nil), 0, 0}
}

// GetSize return the number of bytes read.
func (b *BytesReader) GetSize() int32 {
return b.offset
}

// SetSize sets the size.
// DEPRECATED
func (b *BytesReader) SetSize(size int32) {
// DEPRECATED
}

// HasNextChunk returns true if the size of data is greater than the offset.
func (b *BytesReader) HasNextChunk() bool {
return b.offset < b.size
}

// Rewind set the offset to 0.
func (b *BytesReader) Rewind() {
b.offset = 0
}

// ReadNextChunk return the part of data from offset to the end of bytes array.
// It return always nil errors because the data was already fetched from container.
func (b *BytesReader) ReadNextChunk() ([]byte, error, error) {
if b.size == b.offset {
return []byte{}, nil, nil
}

b.offset = b.size
return b.data, nil, nil
}

// FetchSize read the log from the container and save the any data beyond offset to data field.
// Returns the size of fetched data and container error or connection error.
func (b *BytesReader) FetchSize() (int32, error, error) {
data, n, containerErr, connErr := b.client.ContainerLogs(b.id)
if containerErr != nil || connErr != nil {
return 0, containerErr, connErr
}

if n < b.offset {
b.Rewind()
}
if n > b.offset {
//remove the previous data and keep only the difference between the last received data and the present data.
b.data = nil
b.data = append(b.data, data[b.offset:]...)
b.size = n
}
return n, nil, nil
}

func Close() {
// TO NOTHING
}
Loading

0 comments on commit 688eefb

Please sign in to comment.