mirror of
https://github.com/go-i2p/go-i2p-bt.git
synced 2025-07-06 14:33:14 -04:00
add the file downloading handler
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
|
||||
A pure golang implementation of [BitTorrent](http://bittorrent.org/beps/bep_0000.html) library, which is inspired by [dht](https://github.com/shiyanhui/dht) and [torrent](https://github.com/anacrolix/torrent).
|
||||
|
||||
***WORK IN PROGRESS***
|
||||
|
||||
## Features
|
||||
|
||||
@ -14,7 +15,7 @@ A pure golang implementation of [BitTorrent](http://bittorrent.org/beps/bep_0000
|
||||
|
||||
## Install
|
||||
```shell
|
||||
$ go get github.com/xgfone/bt
|
||||
$ go get -u github.com/xgfone/bt
|
||||
```
|
||||
|
||||
|
||||
|
197
downloader/file_download_handler.go
Normal file
197
downloader/file_download_handler.go
Normal file
@ -0,0 +1,197 @@
|
||||
// Copyright 2020 xgfone
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package downloader
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/xgfone/bt/metainfo"
|
||||
pp "github.com/xgfone/bt/peerprotocol"
|
||||
)
|
||||
|
||||
// PieceBlock is the information of the block in the piece.
|
||||
type PieceBlock struct {
|
||||
Index uint32 // The index of the piece
|
||||
Offset uint32 // The offset from the beginning of the piece
|
||||
Length uint32 // The length of the block, which is equal to 2^14 in general
|
||||
}
|
||||
|
||||
// pieceIndexes is a set of the piece indexes.
|
||||
type pieceIndexes []uint32
|
||||
|
||||
func (ps pieceIndexes) Len() int { return len(ps) }
|
||||
func (ps pieceIndexes) Less(i, j int) bool { return ps[i] < ps[j] }
|
||||
func (ps pieceIndexes) Swap(i, j int) { ps[i], ps[j] = ps[j], ps[i] }
|
||||
|
||||
// Sort sorts itself.
|
||||
func (ps pieceIndexes) Sort() { sort.Sort(ps) }
|
||||
|
||||
// Contains reports whether it contains the given index.
|
||||
func (ps pieceIndexes) Contains(index uint32) bool {
|
||||
for _, p := range ps {
|
||||
if p == index {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// pieceIndexInfo is the information of the piece indexes in the remote peers.
|
||||
type pieceIndexInfo struct {
|
||||
Fasts pieceIndexes
|
||||
Pieces pieceIndexes
|
||||
Suggests pieceIndexes
|
||||
}
|
||||
|
||||
var _ pp.Handler = &FileDownloadHandler{}
|
||||
var _ pp.Bep3Handler = &FileDownloadHandler{}
|
||||
var _ pp.Bep6Handler = &FileDownloadHandler{}
|
||||
|
||||
// FileDownloadHandler is used to downloads the files in the torrent file.
|
||||
type FileDownloadHandler struct {
|
||||
pp.NoopHandler
|
||||
pp.NoopBep3Handler
|
||||
pp.NoopBep6Handler
|
||||
|
||||
Writer *metainfo.Writer // Required
|
||||
|
||||
Logger func(format string, args ...interface{}) // Optional
|
||||
GetNextBlock func(fasts, suggests, pieces []uint32) []PieceBlock // Required
|
||||
}
|
||||
|
||||
// NewFileDownloadHandler returns a new FileDownloadHandler.
|
||||
func NewFileDownloadHandler(writer *metainfo.Writer,
|
||||
getNextBlock func([]uint32, []uint32, []uint32) []PieceBlock,
|
||||
log func(string, ...interface{})) *FileDownloadHandler {
|
||||
return &FileDownloadHandler{
|
||||
Logger: log,
|
||||
Writer: writer,
|
||||
GetNextBlock: getNextBlock,
|
||||
}
|
||||
}
|
||||
|
||||
// OnHandShake implements the interface Handler#OnHandShake.
|
||||
//
|
||||
// Notice: it uses the field Data to store the inner data, you mustn't override
|
||||
// it.
|
||||
func (fd *FileDownloadHandler) OnHandShake(c *pp.PeerConn) (err error) {
|
||||
c.Data = &pieceIndexInfo{Pieces: make([]uint32, 0, fd.Writer.Info.CountPieces())}
|
||||
return
|
||||
}
|
||||
|
||||
// OnMessage implements the interface Handler#OnMessage.
|
||||
func (fd *FileDownloadHandler) OnMessage(c *pp.PeerConn, m pp.Message) error {
|
||||
if log := fd.Logger; log != nil {
|
||||
log("unexpected message '%s' from '%s'", m.Type, c.RemoteAddr().String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestPiece sends the Request to get the piece data.
|
||||
func (fd *FileDownloadHandler) RequestPiece(c *pp.PeerConn) (err error) {
|
||||
pi := c.Data.(*pieceIndexInfo)
|
||||
blocks := fd.GetNextBlock(pi.Fasts, pi.Suggests, pi.Pieces)
|
||||
for _, b := range blocks {
|
||||
if err = c.SendRequest(b.Index, b.Offset, b.Length); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/// ---------------------------------------------------------------------------
|
||||
/// BEP 3
|
||||
|
||||
// Piece implements the interface Bep3Handler#Piece.
|
||||
func (fd *FileDownloadHandler) Piece(c *pp.PeerConn, i, b uint32, p []byte) error {
|
||||
_, err := fd.Writer.WriteBlock(p, i, b)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unchoke implements the interface Bep3Handler#Unchoke.
|
||||
func (fd *FileDownloadHandler) Unchoke(pc *pp.PeerConn) (err error) {
|
||||
return fd.RequestPiece(pc)
|
||||
}
|
||||
|
||||
// Have implements the interface Bep3Handler#Have.
|
||||
func (fd *FileDownloadHandler) Have(pc *pp.PeerConn, index uint32) (err error) {
|
||||
pi := pc.Data.(*pieceIndexInfo)
|
||||
if !pi.Pieces.Contains(index) {
|
||||
pi.Pieces = append(pi.Pieces, index)
|
||||
pi.Pieces.Sort()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Bitfield implements the interface Bep3Handler#Bitfield.
|
||||
func (fd *FileDownloadHandler) Bitfield(pc *pp.PeerConn, bits []bool) (err error) {
|
||||
pi := pc.Data.(*pieceIndexInfo)
|
||||
for i, t := range bits {
|
||||
if t {
|
||||
pi.Pieces = append(pi.Pieces, uint32(i))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/// ---------------------------------------------------------------------------
|
||||
/// BEP 6
|
||||
|
||||
// HaveAll implements the interface Bep6Handler#HaveAll.
|
||||
func (fd *FileDownloadHandler) HaveAll(pc *pp.PeerConn) (err error) {
|
||||
pi := pc.Data.(*pieceIndexInfo)
|
||||
for i := range fd.Writer.Info.Pieces {
|
||||
pi.Pieces = append(pi.Pieces, uint32(i))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Suggest implements the interface Bep6Handler#Suggest.
|
||||
func (fd *FileDownloadHandler) Suggest(pc *pp.PeerConn, index uint32) (err error) {
|
||||
pi := pc.Data.(*pieceIndexInfo)
|
||||
if !pi.Suggests.Contains(index) {
|
||||
pi.Suggests = append(pi.Suggests, index)
|
||||
pi.Suggests.Sort()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AllowedFast implements the interface Bep6Handler#AllowedFast.
|
||||
func (fd *FileDownloadHandler) AllowedFast(pc *pp.PeerConn, index uint32) (err error) {
|
||||
pi := pc.Data.(*pieceIndexInfo)
|
||||
if !pi.Fasts.Contains(index) {
|
||||
pi.Fasts = append(pi.Fasts, index)
|
||||
pi.Fasts.Sort()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Reject implements the interface Bep6Handler#Reject.
|
||||
func (fd *FileDownloadHandler) Reject(pc *pp.PeerConn, index, begin, length uint32) (err error) {
|
||||
pi := pc.Data.(*pieceIndexInfo)
|
||||
pi.Suggests = fd.removePiece(pi.Suggests, index)
|
||||
pi.Pieces = fd.removePiece(pi.Pieces, index)
|
||||
return
|
||||
}
|
||||
|
||||
func (fd *FileDownloadHandler) removePiece(ps pieceIndexes, index uint32) pieceIndexes {
|
||||
for i, p := range ps {
|
||||
if p == index {
|
||||
copy(ps[i:], ps[i+1:])
|
||||
return ps[:len(ps)-1]
|
||||
}
|
||||
}
|
||||
return ps
|
||||
}
|
Reference in New Issue
Block a user