<commit_before>package main

import (
	"github.com/gopherjs/gopherjs/js"
)

const (
	maxKeepaliveInterval = Second * 56
	minReceiveTimeout    = Second * 64
)

// WebSocketTransport runs a reconnect loop for a given host.  It doesn't mind
// disconnections, but a failure to connect and establish a session stops the
// loop.
func WebSocketTransport(s *Session, host string) (connWorked, gotOnline bool) {
	var ws *WebSocket

	defer func() {
		if ws != nil {
			ws.Close()
		}
	}()

	connectTimer := NewTimer(-1)
	defer connectTimer.Stop()

	for {
		gotOnline = false
		hostHealthy := false

		s.log("connecting to", host)

		ws = NewWebSocket("wss://" + host + socketPath)
		connectTimer.Reset(JitterDuration(connectTimeout, 0.1))

		select {
		case connected := <-ws.Notify:
			connectTimer.Stop()

			if connected {
				s.log("connected")
				s.connState("connected")

				connWorked = true
				gotOnline, hostHealthy = webSocketHandshake(s, ws)
			} else {
				s.log("connection failed")
			}

		case <-connectTimer.C:
			s.log("connection timeout")

		case <-s.closeNotify:
			connectTimer.Stop()
		}

		goingAway := ws.GoingAway

		ws.Close()
		ws = nil

		if goingAway {
			s.log("disconnected (server is going away)")
		} else {
			s.log("disconnected")
		}

		if !gotOnline || !hostHealthy || s.stopped || goingAway {
			return
		}
	}
}

// webSocketHandshake creates or resumes a session, and runs I/O loops after
// that.
func webSocketHandshake(s *Session, ws *WebSocket) (gotOnline, hostHealthy bool) {
	var header *js.Object

	if s.sessionId == nil {
		s.log("session creation")
		header = s.makeCreateSessionAction()
	} else {
		s.log("session resumption")
		header = s.makeResumeSessionAction(true)
	}

	if err := ws.SendJSON(header); err != nil {
		s.log("send:", err)
	}

	if s.sessionId == nil {
		var header *js.Object

		timer := NewTimer(JitterDuration(sessionCreateTimeout, 0.2))

		for {
			var err error

			if header, err = ws.ReceiveJSON(); err != nil {
				s.log("session creation:", err)
				return
			}
			if header != nil {
				timer.Stop()
				break
			}

			select {
			case connected := <-ws.Notify:
				if !connected {
					s.log("disconnected during session creation")
					timer.Stop()
					return
				}

			case <-timer.C:
				s.log("session creation timeout")
				return
			}
		}

		if !s.handleSessionEvent(header) {
			return
		}

		gotOnline = true
		hostHealthy = true
		s.connActive()
	}

	fail := make(chan bool, 1)
	done := make(chan bool)

	go webSocketSend(s, ws, fail, done)

	gotEvents, hostHealthy := webSocketReceive(s, ws, fail)
	if gotEvents {
		gotOnline = true
	}

	<-done

	return<commit_msg>websocket: fix session event handling when server disconnects<commit_after>package main

import (
	"github.com/gopherjs/gopherjs/js"
)

const (
	maxKeepaliveInterval = Second * 56
	minReceiveTimeout    = Second * 64
)

// WebSocketTransport runs a reconnect loop for a given host.  It doesn't mind
// disconnections, but a failure to connect and establish a session stops the
// loop.
func WebSocketTransport(s *Session, host string) (connWorked, gotOnline bool) {
	var ws *WebSocket

	defer func() {
		if ws != nil {
			ws.Close()
		}
	}()

	connectTimer := NewTimer(-1)
	defer connectTimer.Stop()

	for {
		gotOnline = false
		hostHealthy := false

		s.log("connecting to", host)

		ws = NewWebSocket("wss://" + host + socketPath)
		connectTimer.Reset(JitterDuration(connectTimeout, 0.1))

		select {
		case connected := <-ws.Notify:
			connectTimer.Stop()

			if connected {
				s.log("connected")
				s.connState("connected")

				connWorked = true
				gotOnline, hostHealthy = webSocketHandshake(s, ws)
			} else {
				s.log("connection failed")
			}

		case <-connectTimer.C:
			s.log("connection timeout")

		case <-s.closeNotify:
			connectTimer.Stop()
		}

		goingAway := ws.GoingAway

		ws.Close()
		ws = nil

		if goingAway {
			s.log("disconnected (server is going away)")
		} else {
			s.log("disconnected")
		}

		if !gotOnline || !hostHealthy || s.stopped || goingAway {
			return
		}
	}
}

// webSocketHandshake creates or resumes a session, and runs I/O loops after
// that.
func webSocketHandshake(s *Session, ws *WebSocket) (gotOnline, hostHealthy bool) {
	var header *js.Object

	if s.sessionId == nil {
		s.log("session creation")
		header = s.makeCreateSessionAction()
	} else {
		s.log("session resumption")
		header = s.makeResumeSessionAction(true)
	}

	if err := ws.SendJSON(header); err != nil {
		s.log("send:", err)
	}

	if s.sessionId == nil {
		var header *js.Object

		connected := true
		timer := NewTimer(JitterDuration(sessionCreateTimeout, 0.2))

		for {
			var err error

			if header, err = ws.ReceiveJSON(); err != nil {
				s.log("session creation:", err)
				return
			}
			if header != nil {
				timer.Stop()
				break
			}

			if !connected {
				s.log("disconnected during session creation")
				timer.Stop()
				return
			}

			select {
			case connected = <-ws.Notify:

			case <-timer.C:
				s.log("session creation timeout")
				return
			}
		}

		if !s.handleSessionEvent(header) {
			return
		}

		gotOnline = true
		hostHealthy = true
		s.connActive()
	}

	fail := make(chan bool, 1)
	done := make(chan bool)

	go webSocketSend(s, ws, fail, done)

	gotEvents, hostHealthy := webSocketReceive(s, ws, fail)
	if gotEvents {
		gotOnline = true
	}

	<-done

	return
{'fraction_non_alphanumeric': 0.12651614107109535, 'fraction_numerical': 0.003732039559619332, 'mean_word_length': 3.1009946442234124, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4655600', 'n_tokens_mistral': 2181, 'n_tokens_neox': 1799, 'n_words': 478}