Implement DCC streaming

This commit is contained in:
Ken-Håvard Lieng 2020-05-20 07:21:12 +02:00
parent 973578bb49
commit e33b9f05e4
15 changed files with 407 additions and 218 deletions

View file

@ -39,11 +39,10 @@ func Serve(handler http.Handler, cfg Config) error {
httpSrv.WriteTimeout = 5 * time.Second
httpsSrv := &http.Server{
Addr: net.JoinHostPort(cfg.Addr, cfg.PortHTTPS),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
Handler: handler,
Addr: net.JoinHostPort(cfg.Addr, cfg.PortHTTPS),
ReadTimeout: 5 * time.Second,
IdleTimeout: 120 * time.Second,
Handler: handler,
}
redirect := HTTPSRedirect(cfg.PortHTTPS, handler)
@ -101,7 +100,6 @@ func Serve(handler http.Handler, cfg Config) error {
}
} else {
httpSrv.ReadTimeout = 5 * time.Second
httpSrv.WriteTimeout = 10 * time.Second
httpSrv.IdleTimeout = 120 * time.Second
httpSrv.Handler = handler

View file

@ -26,12 +26,8 @@ type Client struct {
// Source is the reply to SOURCE CTCP messages
Source string
DownloadFolder string
Autoget bool
Messages chan *Message
ConnectionChanged chan ConnectionState
Progress chan DownloadProgress
Features *Features
nick string
channels []string
@ -59,7 +55,6 @@ func NewClient(nick, username string) *Client {
Realname: nick,
Messages: make(chan *Message, 32),
ConnectionChanged: make(chan ConnectionState, 4),
Progress: make(chan DownloadProgress, 4),
out: make(chan string, 32),
quit: make(chan struct{}),
reconnect: make(chan struct{}),

View file

@ -47,13 +47,6 @@ func (c *Client) handleCTCP(ctcp *CTCP, msg *Message) {
case "CLIENTINFO":
c.ReplyCTCP(msg.Nick, ctcp.Command, ClientInfo)
case "DCC":
if strings.HasPrefix(ctcp.Params, "SEND") {
if dccSend := ParseDCCSend(ctcp); dccSend != nil {
go c.Download(dccSend)
}
}
case "FINGER", "VERSION":
if c.Version != "" {
c.ReplyCTCP(msg.Nick, ctcp.Command, c.Version)

View file

@ -2,14 +2,11 @@ package irc
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"math"
"net"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"time"
@ -52,27 +49,16 @@ func ParseDCCSend(ctcp *CTCP) *DCCSend {
return nil
}
func (c *Client) Download(pack *DCCSend) {
if !c.Autoget {
// TODO: ask user if he/she wants to download the file
return
func DownloadDCC(w io.Writer, pack *DCCSend, progress chan DownloadProgress) error {
if progress != nil {
progress <- DownloadProgress{
File: pack.File,
}
}
c.Progress <- DownloadProgress{
File: pack.File,
}
file, err := os.OpenFile(filepath.Join(c.DownloadFolder, pack.File), os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
c.downloadFailed(pack, err)
return
}
defer file.Close()
conn, err := net.Dial("tcp", net.JoinHostPort(pack.IP, pack.Port))
if err != nil {
c.downloadFailed(pack, err)
return
return err
}
defer conn.Close()
@ -87,64 +73,63 @@ func (c *Client) Download(pack *DCCSend) {
n, err := conn.Read(buf)
if err != nil {
if err != io.EOF {
c.downloadFailed(pack, err)
return
return err
}
if n == 0 {
break
}
}
if _, err := file.Write(buf[:n]); err != nil {
c.downloadFailed(pack, err)
return
if _, err := w.Write(buf[:n]); err != nil {
return err
}
accBytes += uint64(n)
totalBytes += uint64(n)
conn.Write(uint64Bytes(totalBytes))
_, err = conn.Write(uint64Bytes(totalBytes))
if err != nil {
return err
}
if dt := time.Since(prevUpdate); dt >= time.Second {
prevUpdate = time.Now()
if progress != nil {
if dt := time.Since(prevUpdate); dt >= time.Second {
prevUpdate = time.Now()
speed := float64(accBytes) / dt.Seconds()
if averageSpeed == 0 {
averageSpeed = speed
} else {
averageSpeed = 0.2*speed + 0.8*averageSpeed
}
accBytes = 0
speed := float64(accBytes) / dt.Seconds()
if averageSpeed == 0 {
averageSpeed = speed
} else {
averageSpeed = 0.2*speed + 0.8*averageSpeed
}
accBytes = 0
bytesRemaining := float64(pack.Length - totalBytes)
percentage := 100 * (float64(totalBytes) / float64(pack.Length))
bytesRemaining := float64(pack.Length - totalBytes)
percentage := 100 * (float64(totalBytes) / float64(pack.Length))
c.Progress <- DownloadProgress{
Speed: humanReadableByteCount(averageSpeed, true),
PercCompletion: percentage,
BytesRemaining: humanReadableByteCount(bytesRemaining, false),
BytesCompleted: humanReadableByteCount(float64(totalBytes), false),
SecondsElapsed: secondsSince(start),
SecondsToGo: bytesRemaining / averageSpeed,
File: pack.File,
progress <- DownloadProgress{
Speed: humanReadableByteCount(averageSpeed, true),
PercCompletion: percentage,
BytesRemaining: humanReadableByteCount(bytesRemaining, false),
BytesCompleted: humanReadableByteCount(float64(totalBytes), false),
SecondsElapsed: secondsSince(start),
SecondsToGo: bytesRemaining / averageSpeed,
File: pack.File,
}
}
}
}
c.Progress <- DownloadProgress{
PercCompletion: 100,
BytesCompleted: humanReadableByteCount(float64(totalBytes), false),
SecondsElapsed: secondsSince(start),
File: pack.File,
if progress != nil {
progress <- DownloadProgress{
PercCompletion: 100,
BytesCompleted: humanReadableByteCount(float64(totalBytes), false),
SecondsElapsed: secondsSince(start),
File: pack.File,
}
}
}
func (c *Client) downloadFailed(pack *DCCSend, err error) {
c.Progress <- DownloadProgress{
PercCompletion: -1,
File: pack.File,
Error: err,
}
return nil
}
type DownloadProgress struct {
@ -158,14 +143,6 @@ type DownloadProgress struct {
SecondsToGo float64 `json:"eta"`
}
func (p DownloadProgress) ToJSON() string {
progress, err := json.Marshal(p)
if err != nil {
return ""
}
return string(progress)
}
func intToIP(n int) string {
var byte1 = n & 255
var byte2 = ((n >> 8) & 255)