gmessages/libgm/rpc.go

197 lines
5.8 KiB
Go
Raw Normal View History

2023-06-30 11:05:33 +00:00
package libgm
2023-06-30 09:54:08 +00:00
import (
"bufio"
"bytes"
"encoding/json"
2023-06-30 09:54:08 +00:00
"errors"
2023-06-30 11:48:50 +00:00
"fmt"
2023-06-30 09:54:08 +00:00
"io"
"log"
2023-06-30 09:54:08 +00:00
"net/http"
"os"
2023-07-03 21:03:36 +00:00
"time"
2023-06-30 09:54:08 +00:00
"go.mau.fi/mautrix-gmessages/libgm/events"
"go.mau.fi/mautrix-gmessages/libgm/pblite"
"go.mau.fi/mautrix-gmessages/libgm/binary"
2023-06-30 09:54:08 +00:00
"go.mau.fi/mautrix-gmessages/libgm/util"
)
type RPC struct {
client *Client
http *http.Client
conn io.ReadCloser
rpcSessionId string
2023-07-02 14:19:00 +00:00
listenID int
2023-06-30 09:54:08 +00:00
}
func (r *RPC) ListenReceiveMessages(payload []byte) {
2023-07-02 14:19:00 +00:00
r.listenID++
listenID := r.listenID
2023-07-03 21:03:36 +00:00
errored := true
2023-07-02 14:19:00 +00:00
for r.listenID == listenID {
r.client.Logger.Debug().Msg("Starting new long-polling request")
req, err := http.NewRequest("POST", util.RECEIVE_MESSAGES, bytes.NewReader(payload))
if err != nil {
panic(fmt.Errorf("Error creating request: %v", err))
}
util.BuildRelayHeaders(req, "application/json+protobuf", "*/*")
resp, reqErr := r.http.Do(req)
//r.client.Logger.Info().Any("bodyLength", len(payload)).Any("url", util.RECEIVE_MESSAGES).Any("headers", resp.Request.Header).Msg("RPC Request Headers")
if reqErr != nil {
2023-07-03 21:03:36 +00:00
r.client.triggerEvent(&events.ListenTemporaryError{Error: reqErr})
errored = true
r.client.Logger.Err(err).Msg("Error making listen request, retrying in 5 seconds")
time.Sleep(5 * time.Second)
continue
}
if resp.StatusCode >= 400 && resp.StatusCode < 501 {
r.client.triggerEvent(&events.ListenFatalError{Resp: resp})
return
} else if resp.StatusCode >= 500 {
r.client.triggerEvent(&events.ListenTemporaryError{Error: fmt.Errorf("http %d while polling", resp.StatusCode)})
errored = true
r.client.Logger.Debug().Int("statusCode", resp.StatusCode).Msg("5xx error in long polling, retrying in 5 seconds")
time.Sleep(5 * time.Second)
continue
}
if errored {
errored = false
r.client.triggerEvent(&events.ListenRecovered{})
}
2023-07-02 14:19:00 +00:00
r.client.Logger.Debug().Int("statusCode", resp.StatusCode).Msg("Long polling opened")
r.conn = resp.Body
r.startReadingData(resp.Body)
2023-06-30 09:54:08 +00:00
}
}
/*
The start of a message always begins with byte 44 (",")
If the message is parsable (after , has been removed) as an array of interfaces:
func (r *RPC) tryUnmarshalJSON(jsonData []byte, msgArr *[]interface{}) error {
err := json.Unmarshal(jsonData, &msgArr)
return err
}
then the message is complete and it should continue to the HandleRPCMsg function and it should also reset the buffer so that the next message can be received properly.
if it's not parsable, it should just append the received data to the buf and attempt to parse it until it's parsable. Because that would indicate that the full msg has been received
*/
func (r *RPC) startReadingData(rc io.ReadCloser) {
defer rc.Close()
reader := bufio.NewReader(rc)
buf := make([]byte, 2621440)
var accumulatedData []byte
for {
n, err := reader.Read(buf)
if err != nil {
if errors.Is(err, os.ErrClosed) {
r.client.Logger.Err(err).Msg("Closed body from server")
r.conn = nil
return
}
r.client.Logger.Err(err).Msg("Stopped reading data from server")
return
}
chunk := buf[:n]
if n <= 25 { // this will catch the acknowledgement message unless you are required to ack 1000 messages for some reason
isAck := r.isAcknowledgeMessage(chunk)
if isAck {
r.client.Logger.Info().Any("isAck", isAck).Msg("Got Ack Message")
}
2023-06-30 09:54:08 +00:00
isHeartBeat := r.isHeartBeat(chunk)
if isHeartBeat {
r.client.Logger.Info().Any("heartBeat", isHeartBeat).Msg("Got heartbeat message")
}
isStartData := r.isStartRead(chunk)
if isStartData {
r.client.Logger.Info().Any("startRead", isHeartBeat).Msg("Got startReading message")
}
accumulatedData = []byte{}
continue
}
if len(accumulatedData) == 0 {
chunk = bytes.TrimPrefix(chunk, []byte{44})
}
accumulatedData = append(accumulatedData, chunk...)
var msgArr []interface{}
err = r.tryUnmarshalJSON(accumulatedData, &msgArr)
if err != nil {
//r.client.Logger.Err(err).Any("accumulated", string(accumulatedData)).Msg("Unable to unmarshal data, will wait for more data")
continue
}
accumulatedData = []byte{}
//r.client.Logger.Info().Any("val", msgArr).Msg("MsgArr")
2023-06-30 09:54:08 +00:00
go r.HandleRPCMsg(msgArr)
}
}
func (r *RPC) isAcknowledgeMessage(data []byte) bool {
if data[0] == 44 {
return false
}
if len(data) >= 3 && data[0] == 91 && data[1] == 91 && data[2] == 91 {
parsed, parseErr := r.parseAckMessage(data)
if parseErr != nil {
log.Fatal(parseErr)
}
skipCount = parsed.Container.Data.GetAckAmount().Count
r.client.Logger.Info().Any("count", skipCount).Msg("Messages To Skip")
} else {
return false
}
return true
}
func (r *RPC) parseAckMessage(data []byte) (*binary.AckMessageResponse, error) {
data = append(data, 93)
data = append(data, 93)
var msgArr []interface{}
marshalErr := json.Unmarshal(data, &msgArr)
if marshalErr != nil {
return nil, marshalErr
}
msg := &binary.AckMessageResponse{}
deserializeErr := pblite.Deserialize(msgArr, msg.ProtoReflect())
if deserializeErr != nil {
return nil, deserializeErr
}
return msg, nil
}
2023-06-30 09:54:08 +00:00
func (r *RPC) isStartRead(data []byte) bool {
return string(data) == "[[[null,null,null,[]]"
}
func (r *RPC) isHeartBeat(data []byte) bool {
return string(data) == ",[null,null,[]]"
}
func (r *RPC) CloseConnection() {
if r.conn != nil {
r.client.Logger.Debug().Msg("Attempting to connection...")
r.conn.Close()
r.conn = nil
}
}
func (r *RPC) sendMessageRequest(url string, payload []byte) (*http.Response, error) {
req, err := http.NewRequest("POST", url, bytes.NewReader(payload))
if err != nil {
log.Fatalf("Error creating request: %v", err)
2023-06-30 09:54:08 +00:00
}
util.BuildRelayHeaders(req, "application/json+protobuf", "*/*")
resp, reqErr := r.client.http.Do(req)
//r.client.Logger.Info().Any("bodyLength", len(payload)).Any("url", url).Any("headers", resp.Request.Header).Msg("RPC Request Headers")
if reqErr != nil {
log.Fatalf("Error making request: %v", err)
2023-06-30 09:54:08 +00:00
}
return resp, reqErr
}