• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ghettovoice / gosip / 18601286012

17 Oct 2025 06:16PM UTC coverage: 37.451% (-0.7%) from 38.19%
18601286012

push

github

ghettovoice
Update of transports, headers API + transactions matching

- move transport tracker to the core
- replace transport recv iterators with callbacks
- extend sip.Headers with additional methods + small fixes
- client/server transaction matching

80 of 440 new or added lines in 15 files covered. (18.18%)

2 existing lines in 2 files now uncovered.

5351 of 14288 relevant lines covered (37.45%)

5.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.0
/message_request.go
1
package sip
2

3
import (
4
        "fmt"
5
        "io"
6
        "log/slog"
7
        "maps"
8
        "slices"
9
        "strconv"
10

11
        "braces.dev/errtrace"
12

13
        "github.com/ghettovoice/gosip/header"
14
        "github.com/ghettovoice/gosip/internal/core"
15
        "github.com/ghettovoice/gosip/internal/iterutil"
16
        "github.com/ghettovoice/gosip/internal/stringutil"
17
)
18

19
// RequestMethod represents a SIP request method.
20
// See [core.RequestMethod].
21
type RequestMethod = core.RequestMethod
22

23
// Request method constants.
24
// See [core.RequestMethod].
25
const (
26
        RequestMethodAck       = core.RequestMethodAck
27
        RequestMethodBye       = core.RequestMethodBye
28
        RequestMethodCancel    = core.RequestMethodCancel
29
        RequestMethodInfo      = core.RequestMethodInfo
30
        RequestMethodInvite    = core.RequestMethodInvite
31
        RequestMethodMessage   = core.RequestMethodMessage
32
        RequestMethodNotify    = core.RequestMethodNotify
33
        RequestMethodOptions   = core.RequestMethodOptions
34
        RequestMethodPrack     = core.RequestMethodPrack
35
        RequestMethodPublish   = core.RequestMethodPublish
36
        RequestMethodRefer     = core.RequestMethodRefer
37
        RequestMethodRegister  = core.RequestMethodRegister
38
        RequestMethodSubscribe = core.RequestMethodSubscribe
39
        RequestMethodUpdate    = core.RequestMethodUpdate
40
)
41

42
type Request struct {
43
        Method   RequestMethod
44
        URI      URI
45
        Proto    ProtoInfo
46
        Headers  Headers
47
        Body     []byte
48
        Metadata MessageMetadata
49
}
50

51
func (req *Request) RenderTo(w io.Writer, opts ...any) error {
13✔
52
        if req == nil {
14✔
53
                return nil
1✔
54
        }
1✔
55
        if err := req.renderStartLine(w, opts...); err != nil {
12✔
56
                return errtrace.Wrap(err)
×
57
        }
×
58
        if _, err := fmt.Fprint(w, "\r\n"); err != nil {
12✔
59
                return errtrace.Wrap(err)
×
60
        }
×
61
        if err := renderHdrs(w, req.Headers, opts...); err != nil {
12✔
62
                return errtrace.Wrap(err)
×
63
        }
×
64
        if _, err := fmt.Fprint(w, "\r\n"); err != nil {
12✔
65
                return errtrace.Wrap(err)
×
66
        }
×
67
        if _, err := w.Write(req.Body); err != nil {
12✔
68
                return errtrace.Wrap(err)
×
69
        }
×
70
        return nil
12✔
71
}
72

73
func (req *Request) renderStartLine(w io.Writer, opts ...any) error {
14✔
74
        if _, err := fmt.Fprint(w, req.Method, " "); err != nil {
14✔
75
                return errtrace.Wrap(err)
×
76
        }
×
77
        if req.URI != nil {
25✔
78
                if err := req.URI.RenderTo(w, opts...); err != nil {
11✔
79
                        return errtrace.Wrap(err)
×
80
                }
×
81
        }
82
        if _, err := fmt.Fprint(w, " ", req.Proto); err != nil {
14✔
83
                return errtrace.Wrap(err)
×
84
        }
×
85
        return nil
14✔
86
}
87

88
func (req *Request) Render(opts ...any) string {
5✔
89
        if req == nil {
6✔
90
                return ""
1✔
91
        }
1✔
92
        sb := stringutil.GetStringBuilder()
4✔
93
        defer stringutil.FreeStringBuilder(sb)
4✔
94
        _ = req.RenderTo(sb, opts...)
4✔
95
        return sb.String()
4✔
96
}
97

98
func (req *Request) String() string {
3✔
99
        if req == nil {
4✔
100
                return "<nil>"
1✔
101
        }
1✔
102
        sb := stringutil.GetStringBuilder()
2✔
103
        defer stringutil.FreeStringBuilder(sb)
2✔
104
        // TODO make a better short representation of the request
2✔
105
        _ = req.renderStartLine(sb)
2✔
106
        return sb.String()
2✔
107
}
108

109
func (req *Request) Format(f fmt.State, verb rune) {
×
110
        switch verb {
×
111
        case 's':
×
112
                if f.Flag('+') {
×
113
                        _ = req.RenderTo(f)
×
114
                        return
×
115
                }
×
116
                f.Write([]byte(req.String())) //nolint:errcheck
×
117
                return
×
118
        case 'q':
×
119
                f.Write([]byte(strconv.Quote(req.String()))) //nolint:errcheck
×
120
                return
×
121
        default:
×
122
                type hideMethods Request
×
123
                type Request hideMethods
×
124
                fmt.Fprintf(f, fmt.FormatString(f, verb), (*Request)(req))
×
125
                return
×
126
        }
127
}
128

129
func (req *Request) LogValue() slog.Value {
×
130
        if req == nil {
×
131
                return slog.Value{}
×
132
        }
×
133

134
        hdrs := make([]any, 0, 5)
×
NEW
135
        if hop, ok := iterutil.First(req.Headers.Via()); ok {
×
NEW
136
                hdrs = append(hdrs, slog.Any("Via", hop))
×
137
        }
×
NEW
138
        if from, ok := req.Headers.From(); ok {
×
139
                hdrs = append(hdrs, slog.Any("From", from))
×
140
        }
×
NEW
141
        if to, ok := req.Headers.To(); ok {
×
142
                hdrs = append(hdrs, slog.Any("To", to))
×
143
        }
×
NEW
144
        if callID, ok := req.Headers.CallID(); ok {
×
145
                hdrs = append(hdrs, slog.Any("Call-ID", callID))
×
146
        }
×
NEW
147
        if cseq, ok := req.Headers.CSeq(); ok {
×
148
                hdrs = append(hdrs, slog.Any("CSeq", cseq))
×
149
        }
×
150

151
        metadata := make([]any, 0, 8)
×
152
        for _, f := range []string{
×
153
                TransportProtoField,
×
154
                NetworkField,
×
155
                LocalAddrField,
×
156
                RemoteAddrField,
×
157
                RequestTstampField,
×
158
        } {
×
159
                if v, ok := req.Metadata[f]; ok {
×
160
                        metadata = append(metadata, slog.Any(f, v))
×
161
                }
×
162
        }
163

164
        return slog.GroupValue(
×
165
                slog.Any("method", req.Method),
×
166
                slog.Any("uri", req.URI),
×
167
                slog.Group("headers", hdrs...),
×
168
                slog.Group("metadata", metadata...),
×
169
        )
×
170
}
171

172
func (req *Request) Clone() Message {
13✔
173
        if req == nil {
14✔
174
                return nil
1✔
175
        }
1✔
176
        req2 := *req
12✔
177
        req2.URI = core.Clone[URI](req.URI)
12✔
178
        req2.Headers = req.Headers.Clone()
12✔
179
        req2.Body = slices.Clone(req.Body)
12✔
180
        req2.Metadata = maps.Clone(req.Metadata)
12✔
181
        return &req2
12✔
182
}
183

184
func (req *Request) Equal(val any) bool {
45✔
185
        var other *Request
45✔
186
        switch v := val.(type) {
45✔
187
        case Request:
2✔
188
                other = &v
2✔
189
        case *Request:
42✔
190
                other = v
42✔
191
        default:
1✔
192
                return false
1✔
193
        }
194

195
        if req == other {
45✔
196
                return true
1✔
197
        } else if req == nil || other == nil {
46✔
198
                return false
2✔
199
        }
2✔
200

201
        return req.Method.Equal(other.Method) &&
41✔
202
                req.Proto.Equal(other.Proto) &&
41✔
203
                core.IsEqual(req.URI, other.URI) &&
41✔
204
                compareHdrs(req.Headers, other.Headers) &&
41✔
205
                slices.Equal(req.Body, other.Body)
41✔
206
}
207

208
func (req *Request) IsValid() bool {
6✔
209
        return req.Validate() == nil
6✔
210
}
6✔
211

212
func (req *Request) Validate() error {
22✔
213
        if req == nil {
23✔
214
                return errtrace.Wrap(fmt.Errorf("%w: request is nil", ErrInvalidMessage))
1✔
215
        }
1✔
216
        if !req.Method.IsValid() {
22✔
217
                return errtrace.Wrap(fmt.Errorf("%w: invalid method %q", ErrInvalidMessage, req.Method))
1✔
218
        }
1✔
219
        if !core.IsValid(req.URI) {
21✔
220
                return errtrace.Wrap(fmt.Errorf("%w: invalid uri %q", ErrInvalidMessage, req.URI))
1✔
221
        }
1✔
222
        if !req.Proto.IsValid() {
20✔
223
                return errtrace.Wrap(fmt.Errorf("%w: invalid protocol %q", ErrInvalidMessage, req.Proto))
1✔
224
        }
1✔
225
        if err := validateHdrs(req.Headers); err != nil {
19✔
226
                return errtrace.Wrap(fmt.Errorf("%w: %w", ErrInvalidMessage, err))
1✔
227
        }
1✔
228
        for _, n := range []HeaderName{"Via", "From", "To", "Call-ID", "CSeq", "Max-Forwards"} {
107✔
229
                if !req.Headers.Has(n) {
93✔
230
                        return errtrace.Wrap(fmt.Errorf("%w: %w", ErrInvalidMessage, newMissHdrErr(n)))
3✔
231
                }
3✔
232
        }
233
        if ct, ok := FirstHeader[header.ContentLength](req.Headers, "Content-Length"); ok {
21✔
234
                if ct, bl := int(ct), len(req.Body); ct != bl {
7✔
235
                        return errtrace.Wrap(fmt.Errorf("%w: Content-Length %v != body len %d", ErrInvalidMessage, ct, bl))
×
236
                }
×
237
        }
238
        return nil
14✔
239
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc