Reliability fixes: - Fix HTTP response body drainage in xmrig, ttminer, miner - Fix database init race condition (nil before close) - Fix empty minerType bug in P2P StartMinerPayload - Add context timeout to InsertHashratePoint (5s default) Architecture improvements: - Extract HashrateStore interface with DefaultStore/NopStore - Create ServiceContainer for centralized initialization - Extract protocol response handler (ValidateResponse, ParseResponse) - Create generic FileRepository[T] with atomic writes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
88 lines
2.7 KiB
Go
88 lines
2.7 KiB
Go
package node
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// ProtocolError represents an error from the remote peer.
|
|
type ProtocolError struct {
|
|
Code int
|
|
Message string
|
|
}
|
|
|
|
func (e *ProtocolError) Error() string {
|
|
return fmt.Sprintf("remote error (%d): %s", e.Code, e.Message)
|
|
}
|
|
|
|
// ResponseHandler provides helpers for handling protocol responses.
|
|
type ResponseHandler struct{}
|
|
|
|
// ValidateResponse checks if the response is valid and returns a parsed error if it's an error response.
|
|
// It checks:
|
|
// 1. If response is nil (returns error)
|
|
// 2. If response is an error message (returns ProtocolError)
|
|
// 3. If response type matches expected (returns error if not)
|
|
func (h *ResponseHandler) ValidateResponse(resp *Message, expectedType MessageType) error {
|
|
if resp == nil {
|
|
return fmt.Errorf("nil response")
|
|
}
|
|
|
|
// Check for error response
|
|
if resp.Type == MsgError {
|
|
var errPayload ErrorPayload
|
|
if err := resp.ParsePayload(&errPayload); err != nil {
|
|
return &ProtocolError{Code: ErrCodeUnknown, Message: "unable to parse error response"}
|
|
}
|
|
return &ProtocolError{Code: errPayload.Code, Message: errPayload.Message}
|
|
}
|
|
|
|
// Check expected type
|
|
if resp.Type != expectedType {
|
|
return fmt.Errorf("unexpected response type: expected %s, got %s", expectedType, resp.Type)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ParseResponse validates the response and parses the payload into the target.
|
|
// This combines ValidateResponse and ParsePayload into a single call.
|
|
func (h *ResponseHandler) ParseResponse(resp *Message, expectedType MessageType, target interface{}) error {
|
|
if err := h.ValidateResponse(resp, expectedType); err != nil {
|
|
return err
|
|
}
|
|
|
|
if target != nil {
|
|
if err := resp.ParsePayload(target); err != nil {
|
|
return fmt.Errorf("failed to parse %s payload: %w", expectedType, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DefaultResponseHandler is the default response handler instance.
|
|
var DefaultResponseHandler = &ResponseHandler{}
|
|
|
|
// ValidateResponse is a convenience function using the default handler.
|
|
func ValidateResponse(resp *Message, expectedType MessageType) error {
|
|
return DefaultResponseHandler.ValidateResponse(resp, expectedType)
|
|
}
|
|
|
|
// ParseResponse is a convenience function using the default handler.
|
|
func ParseResponse(resp *Message, expectedType MessageType, target interface{}) error {
|
|
return DefaultResponseHandler.ParseResponse(resp, expectedType, target)
|
|
}
|
|
|
|
// IsProtocolError returns true if the error is a ProtocolError.
|
|
func IsProtocolError(err error) bool {
|
|
_, ok := err.(*ProtocolError)
|
|
return ok
|
|
}
|
|
|
|
// GetProtocolErrorCode returns the error code if err is a ProtocolError, otherwise returns 0.
|
|
func GetProtocolErrorCode(err error) int {
|
|
if pe, ok := err.(*ProtocolError); ok {
|
|
return pe.Code
|
|
}
|
|
return 0
|
|
}
|