Refactoring operations on search.go. Fix race condition inside SearchProvider.Search

main
Stefano Scafiti 2022-10-22 13:19:47 +02:00
parent aaed716ed3
commit 1162bdb9fa
4 changed files with 52 additions and 47 deletions

10
main.go
View File

@ -65,12 +65,15 @@ func searchCommand(args []string) {
res, _ := registry.Search(args) res, _ := registry.Search(args)
for _, fileInfo := range res { for _, fileInfo := range res {
printer.AddRow(Row{fileInfo.Name, formatSize(fileInfo.Size), fileInfo.Url}) printer.AddRow(Row{fileInfo.Name, formatSize(fileInfo.Size), fileInfo.URL.String()})
} }
sortColumn := 2
if *sortByFilename { if *sortByFilename {
printer.SortByColumn(0) sortColumn = 0
} }
printer.SortByColumn(sortColumn)
printer.Print() printer.Print()
} }
@ -104,7 +107,6 @@ func suggestUnknownAuthoritySwitch(err error) {
func doTransfer(transfer *XdccTransfer) { func doTransfer(transfer *XdccTransfer) {
err := transfer.Start() err := transfer.Start()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
suggestUnknownAuthoritySwitch(err) suggestUnknownAuthoritySwitch(err)
@ -180,7 +182,7 @@ func getCommand(args []string) {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
for _, urlStr := range urlList { for _, urlStr := range urlList {
if strings.HasPrefix(urlStr, "irc://") { if strings.HasPrefix(urlStr, "irc://") {
url, err := parseIRCFileURl(urlStr) url, err := parseURL(urlStr)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())

View File

@ -14,11 +14,8 @@ import (
) )
type XdccFileInfo struct { type XdccFileInfo struct {
Network string URL IRCFile
Channel string
BotName string
Name string Name string
Url string
Size int64 Size int64
Slot int Slot int
} }
@ -46,25 +43,35 @@ func (registry *XdccProviderRegistry) AddProvider(provider XdccSearchProvider) {
const MaxResults = 1024 const MaxResults = 1024
func (registry *XdccProviderRegistry) Search(keywords []string) ([]XdccFileInfo, error) { func (registry *XdccProviderRegistry) Search(keywords []string) ([]XdccFileInfo, error) {
allResults := make([]XdccFileInfo, 0, MaxResults) allResults := make(map[IRCFile]XdccFileInfo)
mtx := sync.Mutex{}
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(len(registry.providerList)) wg.Add(len(registry.providerList))
for _, p := range registry.providerList { for _, p := range registry.providerList {
go func(p XdccSearchProvider) { go func(p XdccSearchProvider) {
res, err := p.Search(keywords) resList, err := p.Search(keywords)
if err != nil { if err != nil {
return return
} }
allResults = append(allResults, res...) mtx.Lock()
for _, res := range resList {
allResults[res.URL] = res
}
mtx.Unlock()
wg.Done() wg.Done()
}(p) }(p)
} }
wg.Wait() wg.Wait()
return allResults, nil
results := make([]XdccFileInfo, 0, MaxResults)
for _, res := range allResults {
results = append(results, res)
}
return results, nil
} }
func parseFileSize(sizeStr string) (int64, error) { func parseFileSize(sizeStr string) (int64, error) {
@ -102,11 +109,10 @@ func (p *XdccEuProvider) parseFields(fields []string) (*XdccFileInfo, error) {
} }
fInfo := &XdccFileInfo{} fInfo := &XdccFileInfo{}
fInfo.Network = fields[0] fInfo.URL.Network = fields[0]
fInfo.Channel = fields[1] fInfo.URL.Channel = fields[1]
fInfo.BotName = fields[2] fInfo.URL.UserName = fields[2]
slot, err := strconv.Atoi(fields[3][1:]) slot, err := strconv.Atoi(fields[3][1:])
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -149,12 +155,12 @@ func (p *XdccEuProvider) Search(keywords []string) ([]XdccFileInfo, error) {
doc.Find("tr").Each(func(_ int, s *goquery.Selection) { doc.Find("tr").Each(func(_ int, s *goquery.Selection) {
fields := make([]string, 0) fields := make([]string, 0)
var url string var urlStr string
s.Children().Each(func(i int, si *goquery.Selection) { s.Children().Each(func(i int, si *goquery.Selection) {
if i == 1 { if i == 1 {
value, exists := si.Find("a").First().Attr("href") value, exists := si.Find("a").First().Attr("href")
if exists { if exists {
url = value urlStr = value
} }
} }
fields = append(fields, strings.TrimSpace(si.Text())) fields = append(fields, strings.TrimSpace(si.Text()))
@ -162,9 +168,12 @@ func (p *XdccEuProvider) Search(keywords []string) ([]XdccFileInfo, error) {
info, err := p.parseFields(fields) info, err := p.parseFields(fields)
if err == nil { if err == nil {
info.Url = url + "/" + info.BotName + "/" + strconv.Itoa(info.Slot) url, err := parseURL(urlStr + "/" + info.URL.UserName + "/" + strconv.Itoa(info.Slot))
if err == nil {
info.URL = *url
fileInfos = append(fileInfos, *info) fileInfos = append(fileInfos, *info)
} }
}
}) })
return fileInfos, nil return fileInfos, nil
} }
@ -177,11 +186,10 @@ const (
type SunXdccProvider struct{} type SunXdccProvider struct{}
func (p *SunXdccProvider) parseFields(entry *SunXdccEntry, index int) (*XdccFileInfo, error) { func (p *SunXdccProvider) parseFields(entry *SunXdccEntry, index int) (*XdccFileInfo, error) {
info := &XdccFileInfo{}
fInfo := &XdccFileInfo{} info.URL.Network = entry.Network[index]
fInfo.Network = entry.Network[index] info.URL.UserName = entry.Bot[index]
fInfo.BotName = entry.Bot[index] info.URL.Channel = entry.Channel[index]
fInfo.Channel = entry.Channel[index]
slot, err := strconv.Atoi(entry.Packnum[index][1:]) slot, err := strconv.Atoi(entry.Packnum[index][1:])
@ -191,19 +199,14 @@ func (p *SunXdccProvider) parseFields(entry *SunXdccEntry, index int) (*XdccFile
sizeString := strings.TrimLeft(strings.TrimRight(entry.Fsize[index], "]"), "[") sizeString := strings.TrimLeft(strings.TrimRight(entry.Fsize[index], "]"), "[")
fInfo.Size, _ = parseFileSize(sizeString) // ignoring error info.Size, _ = parseFileSize(sizeString) // ignoring error
info.Name = entry.Fname[index]
fInfo.Name = entry.Fname[index]
if err != nil { if err != nil {
return nil, err return nil, err
} }
fInfo.Slot = slot info.Slot = slot
return info, nil
fInfo.Url = "irc://" + fInfo.Network + "/" + strings.TrimLeft(fInfo.Channel, "#") + "/" + fInfo.BotName + "/" + strconv.Itoa(fInfo.Slot)
return fInfo, nil
} }
type SunXdccEntry struct { type SunXdccEntry struct {

14
url.go
View File

@ -7,7 +7,7 @@ import (
"strings" "strings"
) )
type IRCFileURL struct { type IRCFile struct {
Network string Network string
Channel string Channel string
UserName string UserName string
@ -29,8 +29,8 @@ func parseSlot(slotStr string) (int, error) {
return strconv.Atoi(slotStr) return strconv.Atoi(slotStr)
} }
// url has the following format: irc://network/channel/bot/#slot // url has the following format: irc://network/channel/bot/slot
func parseIRCFileURl(url string) (*IRCFileURL, error) { func parseURL(url string) (*IRCFile, error) {
if !strings.HasPrefix(url, "irc://") { if !strings.HasPrefix(url, "irc://") {
return nil, errors.New("not an IRC url") return nil, errors.New("not an IRC url")
} }
@ -45,7 +45,7 @@ func parseIRCFileURl(url string) (*IRCFileURL, error) {
return nil, err return nil, err
} }
fileUrl := &IRCFileURL{ fileUrl := &IRCFile{
Network: fields[0], Network: fields[0],
Channel: fields[1], Channel: fields[1],
UserName: fields[2], UserName: fields[2],
@ -58,10 +58,10 @@ func parseIRCFileURl(url string) (*IRCFileURL, error) {
return fileUrl, nil return fileUrl, nil
} }
func (url *IRCFileURL) GetBot() IRCBot { func (url *IRCFile) GetBot() IRCBot {
return IRCBot{Network: url.Network, Channel: url.Channel, Name: url.UserName} return IRCBot{Network: url.Network, Channel: url.Channel, Name: url.UserName}
} }
func (url *IRCFileURL) String() string { func (url *IRCFile) String() string {
return fmt.Sprintf("irc://%s/%s/%s/#%d", url.Network, url.Channel, url.UserName, url.Slot) return fmt.Sprintf("irc://%s/%s/%s/%d", url.Network, url.Channel, url.UserName, url.Slot)
} }

View File

@ -129,14 +129,14 @@ const maxConnAttempts = 5
type XdccTransfer struct { type XdccTransfer struct {
filePath string filePath string
url IRCFileURL url IRCFile
conn *irc.Conn conn *irc.Conn
connAttempts int connAttempts int
started bool started bool
events chan TransferEvent events chan TransferEvent
} }
func NewXdccTransfer(url IRCFileURL, filePath string, enableSSL bool, skipCertificateCheck bool) *XdccTransfer { func NewXdccTransfer(url IRCFile, filePath string, enableSSL bool, skipCertificateCheck bool) *XdccTransfer {
rand.Seed(time.Now().UTC().UnixNano()) rand.Seed(time.Now().UTC().UnixNano())
nick := IRCClientUserName + strconv.Itoa(int(rand.Uint32())) nick := IRCClientUserName + strconv.Itoa(int(rand.Uint32()))