lux/rpc/lux_rpc_defrag.go
2025-01-24 20:14:49 +02:00

136 lines
3.1 KiB
Go

package rpc
import "lux/proto"
// FSM to handle TCP-window split XML packets
const LUX_XML_REQUEST_BEGIN = "<request"
const LUX_XML_REQUEST_END = "</request>"
const LUX_XML_RESPONSE_BEGIN = "<response"
const LUX_XML_RESPONSE_END = "</response>"
const LUX_XML_ERROR_BEGIN = "<error"
const LUX_XML_ERROR_END = "</error>"
type LuxRpcDefragState int
const (
defragStateOff = 0
defragStateReading = 1
defragStateReadingRequest = 1
defragStateReadingResponse = 2
defragStateReadingError = 3
defragStateHasData = 4
defragStateHasRequest
defragStateHasResponse = 5
defragStateHasError = 6
)
type LuxRpcDefrag struct {
buffer proto.LuxBuffer
state LuxRpcDefragState
beginOff int
endOff int
}
func NewLuxRpcDefrag() LuxRpcDefrag {
return LuxRpcDefrag{
buffer: proto.NewLuxBuffer(),
state: defragStateOff,
beginOff: 0,
endOff: 0,
}
}
func (def *LuxRpcDefrag) HasRequest() bool {
return def.state == defragStateHasRequest
}
func (def *LuxRpcDefrag) HasResponse() bool {
return def.state == defragStateHasResponse
}
func (def *LuxRpcDefrag) HasError() bool {
return def.state == defragStateHasError
}
func matchTag(slice []byte, tag string) bool {
tagLen := len(tag)
if len(slice) >= tagLen {
return string(slice[:tagLen]) == tag
}
return false
}
// will return true if data is complete
func (def *LuxRpcDefrag) Feed(data []byte) bool {
// try find tag opening at beginning
if def.state == defragStateOff {
for i := 0; i < len(data); i++ {
slice := data[i:]
if matchTag(slice, LUX_XML_REQUEST_BEGIN) {
def.state = defragStateReadingRequest
def.beginOff = i + len(LUX_XML_REQUEST_BEGIN)
break
} else if matchTag(slice, LUX_XML_RESPONSE_BEGIN) {
def.state = defragStateReadingResponse
def.beginOff = i + len(LUX_XML_RESPONSE_BEGIN)
break
} else if matchTag(slice, LUX_XML_ERROR_BEGIN) {
def.state = defragStateReadingError
def.beginOff = i + len(LUX_XML_ERROR_BEGIN)
break
}
}
}
// now check if slice has ending tag, if so - complete data
if def.state >= defragStateReading {
for i := def.beginOff; i < len(data); i++ {
slice := data[i:]
if matchTag(slice, LUX_XML_REQUEST_END) {
def.state = defragStateHasRequest
def.endOff = i + len(LUX_XML_REQUEST_END)
break
} else if matchTag(slice, LUX_XML_RESPONSE_END) {
def.state = defragStateHasResponse
def.endOff = i + len(LUX_XML_RESPONSE_END)
break
} else if matchTag(slice, LUX_XML_ERROR_END) {
def.state = defragStateHasError
def.endOff = i + len(LUX_XML_ERROR_END)
break
}
}
// got end tag? push exact 0:endOff slice
if def.state >= defragStateHasData {
def.buffer.WriteBytes(data[:def.endOff])
return true // we got complete data!
} else {
// if there is no end tag, then reset beginOff so it wont
// confuse next Feed call
def.beginOff = 0
}
}
// push whatever data is
def.buffer.WriteBytes(data)
return false
}
func (def *LuxRpcDefrag) GetAndForget() []byte {
data := def.buffer.AllBytes()
// reset state
def.buffer = proto.NewLuxBuffer()
def.state = defragStateOff
def.beginOff = 0
def.endOff = 0
return data
}