Решение на HTTP сваляч от Александър Ангелов

Обратно към всички решения

Към профила на Александър Ангелов

Резултати

  • 8 точки от тестове
  • 0 бонус точки
  • 8 точки общо
  • 14 успешни тест(а)
  • 3 неуспешни тест(а)

Код

package main
import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"sync"
)
type fileReader struct {
err error
mut sync.Mutex
dataWrittenCond *sync.Cond
data []byte
readSize int
filledSize int
}
func (f *fileReader) Read(p []byte) (int, error) {
f.mut.Lock()
defer f.mut.Unlock()
if f.err != nil {
return 0, f.err
}
if len(f.data) == 0 {
// we still doesn't have valid size so we should wait
f.dataWrittenCond.Wait()
}
if f.readSize == len(f.data) {
if f.err != nil {
return 0, f.err
}
return 0, io.EOF
}
size := len(p)
leftSize := f.filledSize - f.readSize
for leftSize == 0 {
if f.err != nil {
return 0, f.err
}
f.dataWrittenCond.Wait()
leftSize = f.filledSize - f.readSize
}
if size > leftSize {
size = leftSize
}
copy(p, f.data[f.readSize:f.readSize+size])
f.readSize += size
return size, f.err
}
func DownloadFile(ctx context.Context, urls []string) io.Reader {
result := &fileReader{}
result.dataWrittenCond = sync.NewCond(&result.mut)
if len(urls) == 0 {
result.err = fmt.Errorf("no valid urls")
return result
}
go func() {
//TODO: test code remove me
transport := &http.Transport{}
client := http.Client{Transport: transport}
validUrls := make([]string, 0, len(urls))
var fileSize int64 = -1
// found out the file size
for _, url := range urls {
response, err := client.Head(url)
if err == nil && response.StatusCode < 400 {
validUrls = append(validUrls, url)
fileSize = response.ContentLength
}
}
if len(validUrls) == 0 {
result.mut.Lock()
result.err = fmt.Errorf("no valid urls")
result.mut.Unlock()
result.dataWrittenCond.Signal()
return
}
if fileSize <= 0 {
result.mut.Lock()
result.err = io.EOF
result.mut.Unlock()
result.dataWrittenCond.Signal()
return
}
// set the max data
result.mut.Lock()
result.data = make([]byte, fileSize)
result.mut.Unlock()
numUrls := int64(len(validUrls))
type fileRange struct {
start int64
end int64
urlToDelete string
}
rangeChan := make(chan fileRange, 1)
rangeChan <- fileRange{0, fileSize, ""}
for ran := range rangeChan {
if ran.urlToDelete != "" {
for i, url := range validUrls {
if url == ran.urlToDelete {
validUrls = append(validUrls[:i], validUrls[i+1:len(validUrls)]...)
break
}
}
}
numUrls = int64(len(validUrls))
if numUrls == 0 {
close(rangeChan)
result.mut.Lock()
result.err = fmt.Errorf("no valid urls")
result.mut.Unlock()
result.dataWrittenCond.Signal()
return
}
chunkSize := ran.end - ran.start
fileChunk := chunkSize / numUrls
extraBytes := chunkSize - (fileChunk * numUrls)
startByte := ran.start
for _, url := range validUrls {
requestSize := fileChunk
if extraBytes > 0 {
requestSize++
extraBytes--
}
go func(startIndex, size int64, url string) {
request, _ := http.NewRequest("GET", url, nil)
request.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", startIndex, startIndex+size-1))
for {
response, err := client.Do(request)
if err != nil || response.StatusCode >= 400 {
rangeChan <- fileRange{startIndex, startIndex + size, url}
return
}
if response.StatusCode == 200 || response.StatusCode == 206 {
d, _ := ioutil.ReadAll(response.Body)
response.Body.Close()
result.mut.Lock()
for int64(result.filledSize) != startIndex {
result.dataWrittenCond.Wait()
}
copy(result.data[startIndex:], d[:])
result.filledSize += len(d)
result.mut.Unlock()
result.dataWrittenCond.Broadcast()
if int64(result.filledSize) == fileSize {
close(rangeChan)
}
if int64(len(d)) != size {
startIndex += int64(len(d))
size -= int64(len(d))
request.Header.Del("Range")
request.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", startIndex, startIndex+size-1))
} else {
break
}
}
}
}(startByte, requestSize, url)
startByte += requestSize
}
}
}()
return result
}

Лог от изпълнението

PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.007s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.006s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.006s
panic: test timed out after 1s

goroutine 15 [running]:
panic(0x668de0, 0xc420115440)
	/usr/local/go/src/runtime/panic.go:500 +0x1a1
testing.startAlarm.func1()
	/usr/local/go/src/testing/testing.go:918 +0x10b
created by time.goFunc
	/usr/local/go/src/time/sleep.go:154 +0x44

goroutine 1 [chan receive]:
testing.(*T).Run(0xc42007e0c0, 0x6d7690, 0x32, 0x6ef4e0, 0xc42004bd01)
	/usr/local/go/src/testing/testing.go:647 +0x316
testing.RunTests.func1(0xc42007e0c0)
	/usr/local/go/src/testing/testing.go:793 +0x6d
testing.tRunner(0xc42007e0c0, 0xc420106e30)
	/usr/local/go/src/testing/testing.go:610 +0x81
testing.RunTests(0x6ef6c8, 0x802140, 0x11, 0x11, 0x7f3d3a5b7000)
	/usr/local/go/src/testing/testing.go:799 +0x2f5
testing.(*M).Run(0xc420106ef8, 0x686ba0)
	/usr/local/go/src/testing/testing.go:743 +0x85
main.main()
	_/tmp/d20170109-30451-ifxh94/_test/_testmain.go:86 +0xc6

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2086 +0x1

goroutine 6 [semacquire]:
sync.runtime_notifyListWait(0xc420018a50, 0x1bc7)
	/usr/local/go/src/runtime/sema.go:267 +0x122
sync.(*Cond).Wait(0xc420018a40)
	/usr/local/go/src/sync/cond.go:57 +0x80
_/tmp/d20170109-30451-ifxh94.(*fileReader).Read(0xc42001c640, 0xc42010e012, 0x5ee, 0x5ee, 0x0, 0x0, 0x0)
	/tmp/d20170109-30451-ifxh94/solution.go:46 +0xd3
bytes.(*Buffer).ReadFrom(0xc42003bef8, 0x7e5860, 0xc42001c640, 0x1, 0x1, 0x7e5860)
	/usr/local/go/src/bytes/buffer.go:176 +0x155
_/tmp/d20170109-30451-ifxh94.TestSingleURLCancelContextAfterHalfBytesWereServed(0xc42007e180)
	/tmp/d20170109-30451-ifxh94/solution_test.go:193 +0x2e7
testing.tRunner(0xc42007e180, 0x6ef4e0)
	/usr/local/go/src/testing/testing.go:610 +0x81
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:646 +0x2ec

goroutine 7 [IO wait]:
net.runtime_pollWait(0x7f3d3a55c178, 0x72, 0x0)
	/usr/local/go/src/runtime/netpoll.go:160 +0x59
net.(*pollDesc).wait(0xc420014370, 0x72, 0xc4200285e0, 0xc4200121b0)
	/usr/local/go/src/net/fd_poll_runtime.go:73 +0x38
net.(*pollDesc).waitRead(0xc420014370, 0x7e78a0, 0xc4200121b0)
	/usr/local/go/src/net/fd_poll_runtime.go:78 +0x34
net.(*netFD).accept(0xc420014310, 0x0, 0x7e64a0, 0xc4200d60a0)
	/usr/local/go/src/net/fd_unix.go:419 +0x238
net.(*TCPListener).accept(0xc42002a048, 0x43418e, 0xc420028690, 0x52d46d)
	/usr/local/go/src/net/tcpsock_posix.go:132 +0x2e
net.(*TCPListener).Accept(0xc42002a048, 0x6ef8a8, 0xc4200f2000, 0x7ea420, 0xc4200dc060)
	/usr/local/go/src/net/tcpsock.go:222 +0x49
net/http.(*Server).Serve(0xc42001a300, 0x7e9ba0, 0xc42002a048, 0x0, 0x0)
	/usr/local/go/src/net/http/server.go:2273 +0x1ce
net/http/httptest.(*Server).goServe.func1(0xc4200604e0)
	/usr/local/go/src/net/http/httptest/server.go:235 +0x6d
created by net/http/httptest.(*Server).goServe
	/usr/local/go/src/net/http/httptest/server.go:236 +0x5c

goroutine 8 [chan receive]:
_/tmp/d20170109-30451-ifxh94.DownloadFile.func1(0xc420013310, 0x1, 0x1, 0xc42001c640)
	/tmp/d20170109-30451-ifxh94/solution.go:116 +0x537
created by _/tmp/d20170109-30451-ifxh94.DownloadFile
	/tmp/d20170109-30451-ifxh94/solution.go:186 +0x1b5

goroutine 12 [runnable]:
net/http.(*persistConn).roundTrip(0xc42000aa00, 0xc42015a020, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/http/transport.go:1840 +0x93b
net/http.(*Transport).RoundTrip(0xc4200c40f0, 0xc4200c42d0, 0xc4200c40f0, 0x0, 0x0)
	/usr/local/go/src/net/http/transport.go:380 +0x4ee
net/http.send(0xc4200c42d0, 0x7e5ea0, 0xc4200c40f0, 0x0, 0x0, 0x0, 0x8, 0xc42028fca0, 0xc42019cdd8)
	/usr/local/go/src/net/http/client.go:256 +0x15f
net/http.(*Client).send(0xc42000d140, 0xc4200c42d0, 0x0, 0x0, 0x0, 0xc42019cdd8, 0x0, 0x1)
	/usr/local/go/src/net/http/client.go:146 +0x102
net/http.(*Client).doFollowingRedirects(0xc42000d140, 0xc4200c42d0, 0x6efa40, 0x3, 0x51f001, 0xc42000d560)
	/usr/local/go/src/net/http/client.go:528 +0x5e5
net/http.(*Client).Do(0xc42000d140, 0xc4200c42d0, 0x5, 0xc42013dfe0, 0xb)
	/usr/local/go/src/net/http/client.go:184 +0x1ea
_/tmp/d20170109-30451-ifxh94.DownloadFile.func1.1(0xc42000d140, 0xc420060a20, 0xc42001c640, 0xc420013330, 0x12, 0x13, 0xc4200c09c0, 0x1c)
	/tmp/d20170109-30451-ifxh94/solution.go:149 +0x20a
created by _/tmp/d20170109-30451-ifxh94.DownloadFile.func1
	/tmp/d20170109-30451-ifxh94/solution.go:181 +0x820

goroutine 10 [runnable]:
net.(*conn).Read(0xc42002a068, 0xc420085000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/net.go:169
net/http.(*persistConn).Read(0xc42000aa00, 0xc420085000, 0x1000, 0x1000, 0x30, 0xc42003cb58, 0x43b23c)
	/usr/local/go/src/net/http/transport.go:1261 +0x154
bufio.(*Reader).fill(0xc420060840)
	/usr/local/go/src/bufio/bufio.go:97 +0x10c
bufio.(*Reader).Peek(0xc420060840, 0x1, 0x0, 0x1, 0x0, 0xc420156780, 0x0)
	/usr/local/go/src/bufio/bufio.go:129 +0x62
net/http.(*persistConn).readLoop(0xc42000aa00)
	/usr/local/go/src/net/http/transport.go:1418 +0x1a1
created by net/http.(*Transport).dialConn
	/usr/local/go/src/net/http/transport.go:1062 +0x4e9

goroutine 11 [select]:
net/http.(*persistConn).writeLoop(0xc42000aa00)
	/usr/local/go/src/net/http/transport.go:1646 +0x3bd
created by net/http.(*Transport).dialConn
	/usr/local/go/src/net/http/transport.go:1063 +0x50e

goroutine 18 [IO wait]:
net.runtime_pollWait(0x7f3d3a55bff8, 0x72, 0x6)
	/usr/local/go/src/runtime/netpoll.go:160 +0x59
net.(*pollDesc).wait(0xc4200ec060, 0x72, 0xc4200367b0, 0xc4200121b0)
	/usr/local/go/src/net/fd_poll_runtime.go:73 +0x38
net.(*pollDesc).waitRead(0xc4200ec060, 0x7e78a0, 0xc4200121b0)
	/usr/local/go/src/net/fd_poll_runtime.go:78 +0x34
net.(*netFD).Read(0xc4200ec000, 0xc4200f8000, 0x1000, 0x1000, 0x0, 0x7e78a0, 0xc4200121b0)
	/usr/local/go/src/net/fd_unix.go:243 +0x1a1
net.(*conn).Read(0xc4200f0000, 0xc4200f8000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/net.go:173 +0x70
net/http.(*connReader).Read(0xc4200d60c0, 0xc4200f8000, 0x1000, 0x1000, 0xc420036918, 0x6d0966, 0x19)
	/usr/local/go/src/net/http/server.go:586 +0x144
bufio.(*Reader).fill(0xc4200f6000)
	/usr/local/go/src/bufio/bufio.go:97 +0x10c
bufio.(*Reader).ReadSlice(0xc4200f6000, 0xa, 0x0, 0x1e, 0xa, 0x33, 0x0)
	/usr/local/go/src/bufio/bufio.go:330 +0xb5
bufio.(*Reader).ReadLine(0xc4200f6000, 0xc420145a40, 0xf0, 0xf0, 0x6c0980, 0x49ffe3, 0x804d78)
	/usr/local/go/src/bufio/bufio.go:359 +0x37
net/textproto.(*Reader).readLineSlice(0xc4200dc150, 0xc420036aa8, 0xc420036aa8, 0x4106d8, 0xf0, 0x6c0980)
	/usr/local/go/src/net/textproto/reader.go:55 +0x5e
net/textproto.(*Reader).ReadLine(0xc4200dc150, 0xc420145a40, 0xc420036b20, 0x401863, 0xc420036c78)
	/usr/local/go/src/net/textproto/reader.go:36 +0x2f
net/http.readRequest(0xc4200f6000, 0xc420036c00, 0xc420145a40, 0x0, 0x0)
	/usr/local/go/src/net/http/request.go:793 +0xa5
net/http.(*conn).readRequest(0xc4200f2000, 0x7ea360, 0xc4200d4140, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/http/server.go:765 +0x10d
net/http.(*conn).serve(0xc4200f2000, 0x7ea360, 0xc4200d4140)
	/usr/local/go/src/net/http/server.go:1532 +0x3d3
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:2293 +0x44d
exit status 2
FAIL	_/tmp/d20170109-30451-ifxh94	1.016s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.010s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.007s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.007s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.007s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.011s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.012s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.009s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.013s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.013s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.007s
PASS
ok  	_/tmp/d20170109-30451-ifxh94	0.267s
panic: test timed out after 1s

goroutine 4 [running]:
panic(0x668de0, 0xc42014cb40)
	/usr/local/go/src/runtime/panic.go:500 +0x1a1
testing.startAlarm.func1()
	/usr/local/go/src/testing/testing.go:918 +0x10b
created by time.goFunc
	/usr/local/go/src/time/sleep.go:154 +0x44

goroutine 1 [chan receive]:
testing.(*T).Run(0xc42008c0c0, 0x6d3243, 0x21, 0x6ef500, 0xc42004bd01)
	/usr/local/go/src/testing/testing.go:647 +0x316
testing.RunTests.func1(0xc42008c0c0)
	/usr/local/go/src/testing/testing.go:793 +0x6d
testing.tRunner(0xc42008c0c0, 0xc42004be20)
	/usr/local/go/src/testing/testing.go:610 +0x81
testing.RunTests(0x6ef6c8, 0x802140, 0x11, 0x11, 0x7f1bef3384b0)
	/usr/local/go/src/testing/testing.go:799 +0x2f5
testing.(*M).Run(0xc42004bee8, 0x686ba0)
	/usr/local/go/src/testing/testing.go:743 +0x85
main.main()
	_/tmp/d20170109-30451-ifxh94/_test/_testmain.go:86 +0xc6

goroutine 17 [syscall, locked to thread]:
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:2086 +0x1

goroutine 21 [semacquire]:
sync.runtime_notifyListWait(0xc420066890, 0x0)
	/usr/local/go/src/runtime/sema.go:267 +0x122
sync.(*Cond).Wait(0xc420066880)
	/usr/local/go/src/sync/cond.go:57 +0x80
_/tmp/d20170109-30451-ifxh94.(*fileReader).Read(0xc420062640, 0xc4200ec000, 0x1, 0x200, 0x0, 0x0, 0x0)
	/tmp/d20170109-30451-ifxh94/solution.go:30 +0x22a
testing/iotest.(*oneByteReader).Read(0xc420069360, 0xc4200ec000, 0x200, 0x200, 0xc420074d80, 0x64, 0xc420036bb8)
	/usr/local/go/src/testing/iotest/reader.go:25 +0x64
bytes.(*Buffer).ReadFrom(0xc420036ef8, 0x7e66e0, 0xc420069360, 0x64, 0x64, 0x7e5860)
	/usr/local/go/src/bytes/buffer.go:176 +0x155
_/tmp/d20170109-30451-ifxh94.TestSlowLingchiWithMaxConnections(0xc42008c180)
	/tmp/d20170109-30451-ifxh94/solution_test.go:900 +0x54c
testing.tRunner(0xc42008c180, 0x6ef500)
	/usr/local/go/src/testing/testing.go:610 +0x81
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:646 +0x2ec

goroutine 22 [IO wait]:
net.runtime_pollWait(0x7f1bef2e1248, 0x72, 0x0)
	/usr/local/go/src/runtime/netpoll.go:160 +0x59
net.(*pollDesc).wait(0xc42005e290, 0x72, 0xc420023de0, 0xc420068080)
	/usr/local/go/src/net/fd_poll_runtime.go:73 +0x38
net.(*pollDesc).waitRead(0xc42005e290, 0x7e78a0, 0xc420068080)
	/usr/local/go/src/net/fd_poll_runtime.go:78 +0x34
net.(*netFD).accept(0xc42005e230, 0x0, 0x7e64a0, 0xc4200103c0)
	/usr/local/go/src/net/fd_unix.go:419 +0x238
net.(*TCPListener).accept(0xc420076040, 0x43418e, 0xc420023e90, 0x52d46d)
	/usr/local/go/src/net/tcpsock_posix.go:132 +0x2e
net.(*TCPListener).Accept(0xc420076040, 0x6ef8a8, 0xc42001a080, 0x7ea420, 0xc42000c150)
	/usr/local/go/src/net/tcpsock.go:222 +0x49
net/http.(*Server).Serve(0xc42008e280, 0x7e9ba0, 0xc420076040, 0x0, 0x0)
	/usr/local/go/src/net/http/server.go:2273 +0x1ce
net/http/httptest.(*Server).goServe.func1(0xc4200644e0)
	/usr/local/go/src/net/http/httptest/server.go:235 +0x6d
created by net/http/httptest.(*Server).goServe
	/usr/local/go/src/net/http/httptest/server.go:236 +0x5c

goroutine 23 [select]:
net/http.(*persistConn).roundTrip(0xc4200d8500, 0xc420125c80, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/http/transport.go:1840 +0x93b
net/http.(*Transport).RoundTrip(0xc4200dc0f0, 0xc42014f2c0, 0xc4200dc0f0, 0x0, 0x0)
	/usr/local/go/src/net/http/transport.go:380 +0x4ee
net/http.send(0xc42014f2c0, 0x7e5ea0, 0xc4200dc0f0, 0x0, 0x0, 0x0, 0x8, 0xc420047c28, 0xc4200765c0)
	/usr/local/go/src/net/http/client.go:256 +0x15f
net/http.(*Client).send(0xc42006d110, 0xc42014f2c0, 0x0, 0x0, 0x0, 0xc4200765c0, 0x0, 0x1)
	/usr/local/go/src/net/http/client.go:146 +0x102
net/http.(*Client).doFollowingRedirects(0xc42006d110, 0xc42014f2c0, 0x6efa40, 0x19, 0x0, 0x0)
	/usr/local/go/src/net/http/client.go:528 +0x5e5
net/http.(*Client).Head(0xc42006d110, 0xc4200d72c0, 0x19, 0xc420097a70, 0x0, 0x0)
	/usr/local/go/src/net/http/client.go:635 +0x93
_/tmp/d20170109-30451-ifxh94.DownloadFile.func1(0xc420074d80, 0x64, 0x64, 0xc420062640)
	/tmp/d20170109-30451-ifxh94/solution.go:77 +0x1e9
created by _/tmp/d20170109-30451-ifxh94.DownloadFile
	/tmp/d20170109-30451-ifxh94/solution.go:186 +0x1b5

goroutine 25 [IO wait]:
net.runtime_pollWait(0x7f1bef2e1188, 0x72, 0x5)
	/usr/local/go/src/runtime/netpoll.go:160 +0x59
net.(*pollDesc).wait(0xc42005e370, 0x72, 0xc4200379d0, 0xc420068080)
	/usr/local/go/src/net/fd_poll_runtime.go:73 +0x38
net.(*pollDesc).waitRead(0xc42005e370, 0x7e78a0, 0xc420068080)
	/usr/local/go/src/net/fd_poll_runtime.go:78 +0x34
net.(*netFD).Read(0xc42005e310, 0xc420099000, 0x1000, 0x1000, 0x0, 0x7e78a0, 0xc420068080)
	/usr/local/go/src/net/fd_unix.go:243 +0x1a1
net.(*conn).Read(0xc420076060, 0xc420099000, 0x1000, 0x1000, 0x0, 0x0, 0x0)
	/usr/local/go/src/net/net.go:173 +0x70
net/http.(*persistConn).Read(0xc4200d8500, 0xc420099000, 0x1000, 0x1000, 0x30, 0xc420037b58, 0x43b23c)
	/usr/local/go/src/net/http/transport.go:1261 +0x154
bufio.(*Reader).fill(0xc420064840)
	/usr/local/go/src/bufio/bufio.go:97 +0x10c
bufio.(*Reader).Peek(0xc420064840, 0x1, 0x0, 0x1, 0x1, 0xc4201585a0, 0x0)
	/usr/local/go/src/bufio/bufio.go:129 +0x62
net/http.(*persistConn).readLoop(0xc4200d8500)
	/usr/local/go/src/net/http/transport.go:1418 +0x1a1
created by net/http.(*Transport).dialConn
	/usr/local/go/src/net/http/transport.go:1062 +0x4e9

goroutine 26 [select]:
net/http.(*persistConn).writeLoop(0xc4200d8500)
	/usr/local/go/src/net/http/transport.go:1646 +0x3bd
created by net/http.(*Transport).dialConn
	/usr/local/go/src/net/http/transport.go:1063 +0x50e

goroutine 3 [sleep]:
time.Sleep(0x989680)
	/usr/local/go/src/runtime/time.go:59 +0xe1
_/tmp/d20170109-30451-ifxh94.TestSlowLingchiWithMaxConnections.func1(0x7e9da0, 0xc420143450, 0xc42015e5a0)
	/tmp/d20170109-30451-ifxh94/solution_test.go:858 +0xe7
net/http.HandlerFunc.ServeHTTP(0xc420066800, 0x7e9da0, 0xc420143450, 0xc42015e5a0)
	/usr/local/go/src/net/http/server.go:1726 +0x44
net/http.serverHandler.ServeHTTP(0xc42008e280, 0x7e9da0, 0xc420143450, 0xc42015e5a0)
	/usr/local/go/src/net/http/server.go:2202 +0x7d
net/http.(*conn).serve(0xc42001a080, 0x7ea360, 0xc420018200)
	/usr/local/go/src/net/http/server.go:1579 +0x4b7
created by net/http.(*Server).Serve
	/usr/local/go/src/net/http/server.go:2293 +0x44d
exit status 2
FAIL	_/tmp/d20170109-30451-ifxh94	1.009s
2017/01/09 06:39:18 http: Accept error: accept tcp 127.0.0.1:44164: accept4: too many open files; retrying in 5ms
panic: send on closed channel

goroutine 3701 [running]:
panic(0x67db20, 0xc4218aca20)
	/usr/local/go/src/runtime/panic.go:500 +0x1a1
_/tmp/d20170109-30451-ifxh94.DownloadFile.func1.1(0xc42000d170, 0xc420116f60, 0xc42001c6e0, 0xc420013438, 0x32, 0x0, 0xc4200be9c0, 0x18)
	/tmp/d20170109-30451-ifxh94/solution.go:151 +0x632
created by _/tmp/d20170109-30451-ifxh94.DownloadFile.func1
	/tmp/d20170109-30451-ifxh94/solution.go:181 +0x820
exit status 2
FAIL	_/tmp/d20170109-30451-ifxh94	0.461s

История (1 версия и 0 коментара)

Александър обнови решението на 03.01.2017 14:41 (преди над 1 година)

+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "sync"
+)
+
+type fileReader struct {
+ err error
+ mut sync.Mutex
+ dataWrittenCond *sync.Cond
+ data []byte
+ readSize int
+ filledSize int
+}
+
+func (f *fileReader) Read(p []byte) (int, error) {
+ f.mut.Lock()
+ defer f.mut.Unlock()
+ if f.err != nil {
+ return 0, f.err
+ }
+
+ if len(f.data) == 0 {
+ // we still doesn't have valid size so we should wait
+ f.dataWrittenCond.Wait()
+ }
+
+ if f.readSize == len(f.data) {
+ if f.err != nil {
+ return 0, f.err
+ }
+ return 0, io.EOF
+ }
+
+ size := len(p)
+ leftSize := f.filledSize - f.readSize
+ for leftSize == 0 {
+ if f.err != nil {
+ return 0, f.err
+ }
+ f.dataWrittenCond.Wait()
+ leftSize = f.filledSize - f.readSize
+ }
+
+ if size > leftSize {
+ size = leftSize
+ }
+
+ copy(p, f.data[f.readSize:f.readSize+size])
+ f.readSize += size
+
+ return size, f.err
+}
+
+func DownloadFile(ctx context.Context, urls []string) io.Reader {
+ result := &fileReader{}
+ result.dataWrittenCond = sync.NewCond(&result.mut)
+ if len(urls) == 0 {
+ result.err = fmt.Errorf("no valid urls")
+ return result
+ }
+
+ go func() {
+ //TODO: test code remove me
+ transport := &http.Transport{}
+ client := http.Client{Transport: transport}
+
+ validUrls := make([]string, 0, len(urls))
+ var fileSize int64 = -1
+ // found out the file size
+ for _, url := range urls {
+ response, err := client.Head(url)
+ if err == nil && response.StatusCode < 400 {
+ validUrls = append(validUrls, url)
+ fileSize = response.ContentLength
+ }
+ }
+
+ if len(validUrls) == 0 {
+ result.mut.Lock()
+ result.err = fmt.Errorf("no valid urls")
+ result.mut.Unlock()
+ result.dataWrittenCond.Signal()
+ return
+ }
+
+ if fileSize <= 0 {
+ result.mut.Lock()
+ result.err = io.EOF
+ result.mut.Unlock()
+ result.dataWrittenCond.Signal()
+ return
+ }
+
+ // set the max data
+ result.mut.Lock()
+ result.data = make([]byte, fileSize)
+ result.mut.Unlock()
+
+ numUrls := int64(len(validUrls))
+
+ type fileRange struct {
+ start int64
+ end int64
+ urlToDelete string
+ }
+
+ rangeChan := make(chan fileRange, 1)
+ rangeChan <- fileRange{0, fileSize, ""}
+
+ for ran := range rangeChan {
+ if ran.urlToDelete != "" {
+ for i, url := range validUrls {
+ if url == ran.urlToDelete {
+ validUrls = append(validUrls[:i], validUrls[i+1:len(validUrls)]...)
+ break
+ }
+ }
+ }
+ numUrls = int64(len(validUrls))
+ if numUrls == 0 {
+ close(rangeChan)
+ result.mut.Lock()
+ result.err = fmt.Errorf("no valid urls")
+ result.mut.Unlock()
+ result.dataWrittenCond.Signal()
+ return
+ }
+ chunkSize := ran.end - ran.start
+ fileChunk := chunkSize / numUrls
+ extraBytes := chunkSize - (fileChunk * numUrls)
+ startByte := ran.start
+ for _, url := range validUrls {
+ requestSize := fileChunk
+ if extraBytes > 0 {
+ requestSize++
+ extraBytes--
+ }
+ go func(startIndex, size int64, url string) {
+ request, _ := http.NewRequest("GET", url, nil)
+ request.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", startIndex, startIndex+size-1))
+
+ for {
+ response, err := client.Do(request)
+ if err != nil || response.StatusCode >= 400 {
+ rangeChan <- fileRange{startIndex, startIndex + size, url}
+ return
+ }
+
+ if response.StatusCode == 200 || response.StatusCode == 206 {
+ d, _ := ioutil.ReadAll(response.Body)
+ response.Body.Close()
+ result.mut.Lock()
+ for int64(result.filledSize) != startIndex {
+ result.dataWrittenCond.Wait()
+ }
+
+ copy(result.data[startIndex:], d[:])
+ result.filledSize += len(d)
+ result.mut.Unlock()
+ result.dataWrittenCond.Broadcast()
+ if int64(result.filledSize) == fileSize {
+ close(rangeChan)
+ }
+
+ if int64(len(d)) != size {
+ startIndex += int64(len(d))
+ size -= int64(len(d))
+ request.Header.Del("Range")
+ request.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", startIndex, startIndex+size-1))
+ } else {
+ break
+ }
+ }
+ }
+ }(startByte, requestSize, url)
+
+ startByte += requestSize
+ }
+ }
+ }()
+
+ return result
+}