I’m stuck on Stage #ZH1.
I keep getting EOF error,after sending the request message
Here are my logs:
[tester::#ZH1] Running tests for Stage #ZH1 (Magnet Links - Receive metadata)
[tester::#ZH1] Running ./your_bittorrent.sh magnet_info "magnet:?xt=urn:btih:c5fb9894bdaba464811b088d806bdd611ba490af&dn=magnet3.gif&tr=http%3A%2F%2F127.0.0.1:41085%2Fannounce"
[tester::#ZH1] Tracker started on address 127.0.0.1:41085...
[tester::#ZH1]
[tester::#ZH1] Peer listening on address: 127.0.0.1:46715
[your_program] Logs from your program will appear here!
[your_program] Tracker URL: http://127.0.0.1:41085/announce
[your_program] Info Hash: c5fb9894bdaba464811b088d806bdd611ba490af
[tester::#ZH1] Waiting to receive handshake message
[tester::#ZH1] Received handshake: [infohash: c5fb9894bdaba464811b088d806bdd611ba490af, peer_id: 746774777672786b626a6d73706d6976716e736a]
[tester::#ZH1]
[tester::#ZH1] Sending back handshake with peer_id: dcc78ef2fe5e2978ff6b759669788f15089402aa
[tester::#ZH1] Sending bitfield message
[tester::#ZH1] Sending extension handshake
[tester::#ZH1] Waiting to receive extension handshake message
[your_program] Peer ID: dcc78ef2fe5e2978ff6b759669788f15089402aa
[tester::#ZH1] Received extension handshake with payload: d1:md11:ut_metadatai20eee
[tester::#ZH1] Checking metadata extension id received
[tester::#ZH1] Waiting to receive metadata request
[tester::#ZH1] Received payload: �d8:msg_typei0e5:piecei0ee
[tester::#ZH1] Sending metadata response
[your_program] Received bitfield message
[your_program] Sending extension handshake...
[your_program] Peer Metadata Extension ID: 202
[your_program] Sending request message...
[your_program] Error reading message length: EOF
[tester::#ZH1] ✓ Tracker URL is correct.
[tester::#ZH1] Expected stdout to contain "Length: 629944", got: "Tracker URL: http://127.0.0.1:41085/announce\nInfo Hash: c5fb9894bdaba464811b088d806bdd611ba490af\nPeer ID: dcc78ef2fe5e2978ff6b759669788f15089402aa\nReceived bitfield message\nSending extension handshake...\nPeer Metadata Extension ID: 202\nSending request message...\nError reading message length: EOF\n"
[tester::#ZH1] Test failed
And here’s a snippet of my code:
package magnet
import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/codecrafters-io/bittorrent-starter-go/cmd/mybittorrent/peers"
"github.com/codecrafters-io/bittorrent-starter-go/cmd/mybittorrent/tcp"
"github.com/jackpal/bencode-go"
"io"
"net"
"net/url"
"regexp"
)
type extensionMsg struct {
M map[string]interface{} `bencode:"m"`
}
type requestMsgPayload struct {
Msg_type int `bencode:"msg_type"`
Piece int `bencode:"piece"`
}
type dataMsgPayload struct {
Msg_type int `bencode:"msg_type"`
Piece int `bencode:"piece"`
Total_size int `bencode:"total_size"`
}
func ParseMagnetLinks(magnetLink string) (string, string) {
infoHashPattern := `xt=urn:btih:([a-fA-F0-9]{40}|[a-zA-Z2-7]{32})`
trackerPattern := `tr=([^&]+)`
trackerURL := ""
reTracker := regexp.MustCompile(trackerPattern)
trackerURLs := reTracker.FindAllStringSubmatch(magnetLink, -1)
for _, match := range trackerURLs {
decodedURL, err := url.QueryUnescape(match[1])
if err != nil {
fmt.Println(err)
return "", ""
}
trackerURL = decodedURL
fmt.Println("Tracker URL:", trackerURL)
}
reInfoHash := regexp.MustCompile(infoHashPattern)
infoHash := reInfoHash.FindStringSubmatch(magnetLink)
if len(infoHash) > 1 {
fmt.Println("Info Hash:", infoHash[1])
}
return trackerURL, infoHash[1]
}
func MagnetHandshake(magnetLink string) {
trackerURL, infoHash := ParseMagnetLinks(magnetLink)
byteInfoHash, _ := hex.DecodeString(infoHash)
var infoHashArray [20]byte
copy(infoHashArray[:], byteInfoHash)
peerList, err := peers.FetchPeersFromTracker(trackerURL, infoHashArray, nil)
if err != nil || len(peerList) == 0 {
fmt.Println("Error fetching peers or no peers available:", err)
return
}
peerTCPAddr, err := net.ResolveTCPAddr("tcp", peerList[0])
if err != nil {
fmt.Println("Error resolving TCP address:", err)
return
}
tcpConn, err := net.DialTCP("tcp", nil, peerTCPAddr)
if err != nil {
fmt.Println("Error establishing TCP connection:", err)
return
}
defer tcpConn.Close()
peerID := tcp.CompleteHandshake(tcpConn, infoHashArray)
fmt.Println("Peer ID:", peerID)
sendExtensionHandshake(tcpConn)
}
func sendExtensionHandshake(tcpConn *net.TCPConn) {
var peerMetaDataExtensionID int
requestMsgSent := false
for {
messageLength := make([]byte, 4)
_, err := io.ReadFull(tcpConn, messageLength)
if err != nil {
fmt.Println("Error reading message length:", err)
return
}
length := binary.BigEndian.Uint32(messageLength)
if length == 0 {
fmt.Println("Keep alive message received")
return
}
messageID := make([]byte, 1)
_, err = io.ReadFull(tcpConn, messageID)
if err != nil {
fmt.Println("Error reading message ID:", err)
return
}
id := uint8(messageID[0])
switch id {
case 5:
fmt.Println("Received bitfield message")
payload := make([]byte, length-1)
_, err := io.ReadFull(tcpConn, payload)
if err != nil {
fmt.Println("Error reading bitfield payload:", err)
return
}
bencodedDict := map[string]interface{}{
"m": map[string]uint8{"ut_metadata": 20},
}
var bencodedDictBytesBuffer bytes.Buffer
err = bencode.Marshal(&bencodedDictBytesBuffer, bencodedDict)
if err != nil {
fmt.Println("Error encoding bencoded dictionary:", err)
return
}
extensionPayload := bencodedDictBytesBuffer.Bytes()
messageLen := len(extensionPayload) + 2
extensionHandshake := make([]byte, 4+messageLen)
binary.BigEndian.PutUint32(extensionHandshake[0:4], uint32(messageLen))
extensionHandshake[4] = 20
extensionHandshake[5] = 0
copy(extensionHandshake[6:], extensionPayload)
fmt.Println("Sending extension handshake...")
_, err = tcpConn.Write(extensionHandshake)
if err != nil {
fmt.Println("Error sending extension handshake:", err)
}
case 20:
payload := make([]byte, length-1)
_, err = io.ReadFull(tcpConn, payload)
if err != nil {
fmt.Println("Error reading extension message payload:", err)
return
}
extensionMsgID := payload[0]
dict := payload[1:]
buf := bytes.NewReader(dict)
if extensionMsgID == 0 {
extensionMsg := extensionMsg{}
err := bencode.Unmarshal(buf, &extensionMsg)
if err != nil {
fmt.Println("Error unmarshaling extension message:", err)
return
}
if metadataExtID, ok := extensionMsg.M["ut_metadata"].(int); ok {
peerMetaDataExtensionID = metadataExtID
fmt.Println("Peer Metadata Extension ID:", peerMetaDataExtensionID)
requestMsgPayload := requestMsgPayload{
Msg_type: 0,
Piece: 0,
}
var requestMsgPayloadBuf bytes.Buffer
err = bencode.Marshal(&requestMsgPayloadBuf, requestMsgPayload)
if err != nil {
fmt.Println("Error marshaling request payload:", err)
return
}
payloadLength := len(requestMsgPayloadBuf.Bytes()) + 2
requestMsg := make([]byte, 4+payloadLength)
binary.BigEndian.PutUint32(requestMsg[:4], uint32(payloadLength))
requestMsg[4] = 20
requestMsg[5] = uint8(peerMetaDataExtensionID)
copy(requestMsg[6:], requestMsgPayloadBuf.Bytes())
fmt.Println("Sending request message...")
_, err = tcpConn.Write(requestMsg)
if err != nil {
fmt.Println("Error sending request message:", err)
return
}
requestMsgSent = true
} else {
fmt.Println("Could not extract metadata extension ID")
return
}
} else if extensionMsgID == uint8(peerMetaDataExtensionID) && requestMsgSent {
dataMsgPayload := dataMsgPayload{}
err := bencode.Unmarshal(buf, &dataMsgPayload)
if err != nil {
fmt.Println("Error unmarshaling data message:", err)
return
}
fmt.Println("Metadata response received:")
fmt.Println("Msg Type:", dataMsgPayload.Msg_type)
fmt.Println("Piece:", dataMsgPayload.Piece)
fmt.Println("Total Size:", dataMsgPayload.Total_size)
// Break the loop after receiving the metadata response
// break
}
}
}
}