Fix installer to not explode on long lines; closes pterodactyl/panel#2773
This commit is contained in:
parent
59c30c2842
commit
22c53c365a
|
@ -506,21 +506,15 @@ func (ip *InstallationProcess) StreamOutput(ctx context.Context, id string) erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
|
|
||||||
s := bufio.NewScanner(reader)
|
evts := ip.Server.Events()
|
||||||
for s.Scan() {
|
err = system.ScanReader(reader, func(line string) {
|
||||||
ip.Server.Events().Publish(InstallOutputEvent, s.Text())
|
evts.Publish(InstallOutputEvent, line)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ip.Server.Log().WithFields(log.Fields{"container_id": id, "error": err}).Warn("error processing install output lines")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.Err(); err != nil {
|
|
||||||
ip.Server.Log().WithFields(log.Fields{
|
|
||||||
"container_id": id,
|
|
||||||
"error": err,
|
|
||||||
}).Warn("error processing scanner line in installation output for server")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,54 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var cr = []byte(" \r")
|
||||||
|
var crr = []byte("\r\n")
|
||||||
|
|
||||||
|
func ScanReader(r io.Reader, callback func(line string)) error {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
// Avoid constantly re-allocating memory when we're flooding lines through this
|
||||||
|
// function by using the same buffer for the duration of the call and just truncating
|
||||||
|
// the value back to 0 every loop.
|
||||||
|
var str strings.Builder
|
||||||
|
for {
|
||||||
|
str.Reset()
|
||||||
|
var err error
|
||||||
|
var line []byte
|
||||||
|
var isPrefix bool
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Read the line and write it to the buffer.
|
||||||
|
line, isPrefix, err = br.ReadLine()
|
||||||
|
// Certain games like Minecraft output absolutely random carriage returns in the output seemingly
|
||||||
|
// in line with that it thinks is the terminal size. Those returns break a lot of output handling,
|
||||||
|
// so we'll just replace them with proper new-lines and then split it later and send each line as
|
||||||
|
// its own event in the response.
|
||||||
|
str.Write(bytes.Replace(line, cr, crr, -1))
|
||||||
|
// Finish this loop and begin outputting the line if there is no prefix (the line fit into
|
||||||
|
// the default buffer), or if we hit the end of the line.
|
||||||
|
if !isPrefix || err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// If we encountered an error with something in ReadLine that was not an EOF just abort
|
||||||
|
// the entire process here.
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Publish the line for this loop. Break on new-line characters so every line is sent as a single
|
||||||
|
// output event, otherwise you get funky handling in the browser console.
|
||||||
|
for _, line := range strings.Split(str.String(), "\r\n") {
|
||||||
|
callback(line)
|
||||||
|
}
|
||||||
|
// If the error we got previously that lead to the line being output is an io.EOF we want to
|
||||||
|
// exit the entire looping process.
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Runs a given work function every "d" duration until the provided context is canceled.
|
// Runs a given work function every "d" duration until the provided context is canceled.
|
||||||
func Every(ctx context.Context, d time.Duration, work func(t time.Time)) {
|
func Every(ctx context.Context, d time.Duration, work func(t time.Time)) {
|
||||||
ticker := time.NewTicker(d)
|
ticker := time.NewTicker(d)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user