[PHASE-7] Add encoding and decoding for client
This commit is contained in:
parent
71b5d7ed27
commit
2c8d6c828f
41
internal/protocol/encoder.go
Normal file
41
internal/protocol/encoder.go
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// encode is a helper function that encodes any message with the given message type
|
||||||
|
func encode(w io.Writer, msgType MessageType, payload interface{}) error {
|
||||||
|
var payloadBytes []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Only marshal if payload is not nil
|
||||||
|
if payload != nil {
|
||||||
|
payloadBytes, err = json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to encode payload: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write message type (1 byte)
|
||||||
|
if err := binary.Write(w, binary.BigEndian, msgType); err != nil {
|
||||||
|
return fmt.Errorf("failed to write message type: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write payload length (4 bytes, big-endian)
|
||||||
|
if err := binary.Write(w, binary.BigEndian, uint32(len(payloadBytes))); err != nil {
|
||||||
|
return fmt.Errorf("failed to write payload length: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write JSON payload if we have one
|
||||||
|
if len(payloadBytes) > 0 {
|
||||||
|
if _, err := w.Write(payloadBytes); err != nil {
|
||||||
|
return fmt.Errorf("failed to write payload: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"hash-of-wisdom/internal/pow/challenge"
|
"hash-of-wisdom/internal/pow/challenge"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// ChallengeRequest is empty (no payload for challenge requests)
|
// ChallengeRequest is empty (no payload for challenge requests)
|
||||||
type ChallengeRequest struct{}
|
type ChallengeRequest struct{}
|
||||||
|
|
||||||
|
|
@ -16,6 +17,11 @@ func (r *ChallengeRequest) Decode(stream io.Reader) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode writes a challenge request to the writer
|
||||||
|
func (r *ChallengeRequest) Encode(w io.Writer) error {
|
||||||
|
return encode(w, ChallengeRequestType, nil)
|
||||||
|
}
|
||||||
|
|
||||||
// SolutionRequest contains the client's solution attempt
|
// SolutionRequest contains the client's solution attempt
|
||||||
type SolutionRequest struct {
|
type SolutionRequest struct {
|
||||||
Challenge challenge.Challenge `json:"challenge"`
|
Challenge challenge.Challenge `json:"challenge"`
|
||||||
|
|
@ -33,3 +39,8 @@ func (r *SolutionRequest) Decode(stream io.Reader) error {
|
||||||
decoder := json.NewDecoder(stream)
|
decoder := json.NewDecoder(stream)
|
||||||
return decoder.Decode(r)
|
return decoder.Decode(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encode writes a solution request to the writer
|
||||||
|
func (r *SolutionRequest) Encode(w io.Writer) error {
|
||||||
|
return encode(w, SolutionRequestType, r)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,46 @@
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"hash-of-wisdom/internal/pow/challenge"
|
"hash-of-wisdom/internal/pow/challenge"
|
||||||
"hash-of-wisdom/internal/quotes"
|
"hash-of-wisdom/internal/quotes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// writeHeader writes the message type and payload length to the writer
|
|
||||||
func writeHeader(w io.Writer, msgType MessageType, payloadLength uint32) error {
|
|
||||||
// Write message type (1 byte)
|
|
||||||
if err := binary.Write(w, binary.BigEndian, msgType); err != nil {
|
|
||||||
return fmt.Errorf("failed to write message type: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write payload length (4 bytes, big-endian)
|
|
||||||
if err := binary.Write(w, binary.BigEndian, payloadLength); err != nil {
|
|
||||||
return fmt.Errorf("failed to write payload length: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodeResponse is a helper function that encodes any response with the given message type
|
|
||||||
func encodeResponse(w io.Writer, msgType MessageType, payload interface{}) error {
|
|
||||||
// Marshal to get exact payload size
|
|
||||||
payloadBytes, err := json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to encode payload: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write header
|
|
||||||
if err := writeHeader(w, msgType, uint32(len(payloadBytes))); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write JSON payload directly to stream
|
|
||||||
if len(payloadBytes) > 0 {
|
|
||||||
if _, err := w.Write(payloadBytes); err != nil {
|
|
||||||
return fmt.Errorf("failed to write payload: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChallengeResponse represents a challenge response
|
// ChallengeResponse represents a challenge response
|
||||||
type ChallengeResponse struct {
|
type ChallengeResponse struct {
|
||||||
Challenge *challenge.Challenge
|
Challenge *challenge.Challenge
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode reads a challenge response from the payload stream
|
||||||
|
func (r *ChallengeResponse) Decode(stream io.Reader) error {
|
||||||
|
if stream == nil {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON directly from stream
|
||||||
|
decoder := json.NewDecoder(stream)
|
||||||
|
return decoder.Decode(&r.Challenge)
|
||||||
|
}
|
||||||
|
|
||||||
// SolutionResponse represents a successful solution response (contains quote)
|
// SolutionResponse represents a successful solution response (contains quote)
|
||||||
type SolutionResponse struct {
|
type SolutionResponse struct {
|
||||||
Quote *quotes.Quote
|
Quote *quotes.Quote
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode reads a solution response from the payload stream
|
||||||
|
func (r *SolutionResponse) Decode(stream io.Reader) error {
|
||||||
|
if stream == nil {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON directly from stream
|
||||||
|
decoder := json.NewDecoder(stream)
|
||||||
|
return decoder.Decode(&r.Quote)
|
||||||
|
}
|
||||||
|
|
||||||
// ErrorResponse represents an error response
|
// ErrorResponse represents an error response
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
Code string `json:"code"`
|
Code string `json:"code"`
|
||||||
|
|
@ -66,17 +49,28 @@ type ErrorResponse struct {
|
||||||
Details map[string]string `json:"details,omitempty"`
|
Details map[string]string `json:"details,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode reads an error response from the payload stream
|
||||||
|
func (r *ErrorResponse) Decode(stream io.Reader) error {
|
||||||
|
if stream == nil {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON directly from stream
|
||||||
|
decoder := json.NewDecoder(stream)
|
||||||
|
return decoder.Decode(r)
|
||||||
|
}
|
||||||
|
|
||||||
// Encode writes the challenge response to the writer
|
// Encode writes the challenge response to the writer
|
||||||
func (r *ChallengeResponse) Encode(w io.Writer) error {
|
func (r *ChallengeResponse) Encode(w io.Writer) error {
|
||||||
return encodeResponse(w, ChallengeResponseType, r.Challenge)
|
return encode(w, ChallengeResponseType, r.Challenge)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the solution response to the writer
|
// Encode writes the solution response to the writer
|
||||||
func (r *SolutionResponse) Encode(w io.Writer) error {
|
func (r *SolutionResponse) Encode(w io.Writer) error {
|
||||||
return encodeResponse(w, QuoteResponseType, r.Quote)
|
return encode(w, QuoteResponseType, r.Quote)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the error response to the writer
|
// Encode writes the error response to the writer
|
||||||
func (r *ErrorResponse) Encode(w io.Writer) error {
|
func (r *ErrorResponse) Encode(w io.Writer) error {
|
||||||
return encodeResponse(w, ErrorResponseType, r)
|
return encode(w, ErrorResponseType, r)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue