Skip to content

Commit 710bf50

Browse files
authored
Merge pull request #185 from Strazz1337/fix-adhere-to-otel-span
fix(spans): adhere attribute name to otel semver
2 parents d695eb6 + 9cce1d8 commit 710bf50

7 files changed

Lines changed: 413 additions & 373 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.4.7] - 2024-12-13
11+
12+
### Changed
13+
14+
- Updated HTTP span attributes to comply with updated OpenTelemetry semantic conventions. [#182](https://github.com/microsoft/kiota-http-go/issues/182)
15+
1016
## [1.4.6] - 2024-12-13
1117

1218
### Changed

compression_handler.go

Lines changed: 161 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,161 @@
1-
package nethttplibrary
2-
3-
import (
4-
"bytes"
5-
"compress/gzip"
6-
"io"
7-
"net/http"
8-
"strings"
9-
10-
abstractions "github.com/microsoft/kiota-abstractions-go"
11-
"go.opentelemetry.io/otel"
12-
"go.opentelemetry.io/otel/attribute"
13-
"go.opentelemetry.io/otel/trace"
14-
)
15-
16-
// CompressionHandler represents a compression middleware
17-
type CompressionHandler struct {
18-
options CompressionOptions
19-
}
20-
21-
// CompressionOptions is a configuration object for the CompressionHandler middleware
22-
type CompressionOptions struct {
23-
enableCompression bool
24-
}
25-
26-
type compression interface {
27-
abstractions.RequestOption
28-
ShouldCompress() bool
29-
}
30-
31-
var compressKey = abstractions.RequestOptionKey{Key: "CompressionHandler"}
32-
33-
// NewCompressionHandler creates an instance of a compression middleware
34-
func NewCompressionHandler() *CompressionHandler {
35-
options := NewCompressionOptions(true)
36-
return NewCompressionHandlerWithOptions(options)
37-
}
38-
39-
// NewCompressionHandlerWithOptions creates an instance of the compression middleware with
40-
// specified configurations.
41-
func NewCompressionHandlerWithOptions(option CompressionOptions) *CompressionHandler {
42-
return &CompressionHandler{options: option}
43-
}
44-
45-
// NewCompressionOptions creates a configuration object for the CompressionHandler
46-
func NewCompressionOptions(enableCompression bool) CompressionOptions {
47-
return CompressionOptions{enableCompression: enableCompression}
48-
}
49-
50-
// GetKey returns CompressionOptions unique name in context object
51-
func (o CompressionOptions) GetKey() abstractions.RequestOptionKey {
52-
return compressKey
53-
}
54-
55-
// ShouldCompress reads compression setting form CompressionOptions
56-
func (o CompressionOptions) ShouldCompress() bool {
57-
return o.enableCompression
58-
}
59-
60-
// Intercept is invoked by the middleware pipeline to either move the request/response
61-
// to the next middleware in the pipeline
62-
func (c *CompressionHandler) Intercept(pipeline Pipeline, middlewareIndex int, req *http.Request) (*http.Response, error) {
63-
reqOption, ok := req.Context().Value(compressKey).(compression)
64-
if !ok {
65-
reqOption = c.options
66-
}
67-
68-
obsOptions := GetObservabilityOptionsFromRequest(req)
69-
ctx := req.Context()
70-
var span trace.Span
71-
if obsOptions != nil {
72-
ctx, span = otel.GetTracerProvider().Tracer(obsOptions.GetTracerInstrumentationName()).Start(ctx, "CompressionHandler_Intercept")
73-
span.SetAttributes(attribute.Bool("com.microsoft.kiota.handler.compression.enable", true))
74-
defer span.End()
75-
req = req.WithContext(ctx)
76-
}
77-
78-
if !reqOption.ShouldCompress() || contentRangeBytesIsPresent(req.Header) || contentEncodingIsPresent(req.Header) || req.Body == nil {
79-
return pipeline.Next(req, middlewareIndex)
80-
}
81-
if span != nil {
82-
span.SetAttributes(attribute.Bool("http.request_body_compressed", true))
83-
}
84-
85-
unCompressedBody, err := io.ReadAll(req.Body)
86-
unCompressedContentLength := req.ContentLength
87-
if err != nil {
88-
if span != nil {
89-
span.RecordError(err)
90-
}
91-
return nil, err
92-
}
93-
94-
compressedBody, size, err := compressReqBody(unCompressedBody)
95-
if err != nil {
96-
if span != nil {
97-
span.RecordError(err)
98-
}
99-
return nil, err
100-
}
101-
102-
req.Header.Set("Content-Encoding", "gzip")
103-
req.Body = compressedBody
104-
req.ContentLength = int64(size)
105-
106-
if span != nil {
107-
span.SetAttributes(attribute.Int64("http.request_content_length", req.ContentLength))
108-
}
109-
110-
// Sending request with compressed body
111-
resp, err := pipeline.Next(req, middlewareIndex)
112-
if err != nil {
113-
return nil, err
114-
}
115-
116-
// If response has status 415 retry request with uncompressed body
117-
if resp.StatusCode == 415 {
118-
delete(req.Header, "Content-Encoding")
119-
req.Body = io.NopCloser(bytes.NewBuffer(unCompressedBody))
120-
req.ContentLength = unCompressedContentLength
121-
122-
if span != nil {
123-
span.SetAttributes(attribute.Int64("http.request_content_length", req.ContentLength),
124-
attribute.Int("http.request_content_length", 415))
125-
}
126-
127-
return pipeline.Next(req, middlewareIndex)
128-
}
129-
130-
return resp, nil
131-
}
132-
133-
func contentRangeBytesIsPresent(header http.Header) bool {
134-
contentRanges, _ := header["Content-Range"]
135-
for _, contentRange := range contentRanges {
136-
if strings.Contains(strings.ToLower(contentRange), "bytes") {
137-
return true
138-
}
139-
}
140-
return false
141-
}
142-
143-
func contentEncodingIsPresent(header http.Header) bool {
144-
_, ok := header["Content-Encoding"]
145-
return ok
146-
}
147-
148-
func compressReqBody(reqBody []byte) (io.ReadSeekCloser, int, error) {
149-
var buffer bytes.Buffer
150-
gzipWriter := gzip.NewWriter(&buffer)
151-
if _, err := gzipWriter.Write(reqBody); err != nil {
152-
return nil, 0, err
153-
}
154-
155-
if err := gzipWriter.Close(); err != nil {
156-
return nil, 0, err
157-
}
158-
159-
reader := bytes.NewReader(buffer.Bytes())
160-
return NopCloser(reader), buffer.Len(), nil
161-
}
1+
package nethttplibrary
2+
3+
import (
4+
"bytes"
5+
"compress/gzip"
6+
"io"
7+
"net/http"
8+
"strings"
9+
10+
abstractions "github.com/microsoft/kiota-abstractions-go"
11+
"go.opentelemetry.io/otel"
12+
"go.opentelemetry.io/otel/attribute"
13+
"go.opentelemetry.io/otel/trace"
14+
)
15+
16+
// CompressionHandler represents a compression middleware
17+
type CompressionHandler struct {
18+
options CompressionOptions
19+
}
20+
21+
// CompressionOptions is a configuration object for the CompressionHandler middleware
22+
type CompressionOptions struct {
23+
enableCompression bool
24+
}
25+
26+
type compression interface {
27+
abstractions.RequestOption
28+
ShouldCompress() bool
29+
}
30+
31+
var compressKey = abstractions.RequestOptionKey{Key: "CompressionHandler"}
32+
33+
// NewCompressionHandler creates an instance of a compression middleware
34+
func NewCompressionHandler() *CompressionHandler {
35+
options := NewCompressionOptions(true)
36+
return NewCompressionHandlerWithOptions(options)
37+
}
38+
39+
// NewCompressionHandlerWithOptions creates an instance of the compression middleware with
40+
// specified configurations.
41+
func NewCompressionHandlerWithOptions(option CompressionOptions) *CompressionHandler {
42+
return &CompressionHandler{options: option}
43+
}
44+
45+
// NewCompressionOptions creates a configuration object for the CompressionHandler
46+
func NewCompressionOptions(enableCompression bool) CompressionOptions {
47+
return CompressionOptions{enableCompression: enableCompression}
48+
}
49+
50+
// GetKey returns CompressionOptions unique name in context object
51+
func (o CompressionOptions) GetKey() abstractions.RequestOptionKey {
52+
return compressKey
53+
}
54+
55+
// ShouldCompress reads compression setting form CompressionOptions
56+
func (o CompressionOptions) ShouldCompress() bool {
57+
return o.enableCompression
58+
}
59+
60+
// Intercept is invoked by the middleware pipeline to either move the request/response
61+
// to the next middleware in the pipeline
62+
func (c *CompressionHandler) Intercept(pipeline Pipeline, middlewareIndex int, req *http.Request) (*http.Response, error) {
63+
reqOption, ok := req.Context().Value(compressKey).(compression)
64+
if !ok {
65+
reqOption = c.options
66+
}
67+
68+
obsOptions := GetObservabilityOptionsFromRequest(req)
69+
ctx := req.Context()
70+
var span trace.Span
71+
if obsOptions != nil {
72+
ctx, span = otel.GetTracerProvider().Tracer(obsOptions.GetTracerInstrumentationName()).Start(ctx, "CompressionHandler_Intercept")
73+
span.SetAttributes(attribute.Bool("com.microsoft.kiota.handler.compression.enable", true))
74+
defer span.End()
75+
req = req.WithContext(ctx)
76+
}
77+
78+
if !reqOption.ShouldCompress() || contentRangeBytesIsPresent(req.Header) || contentEncodingIsPresent(req.Header) || req.Body == nil {
79+
return pipeline.Next(req, middlewareIndex)
80+
}
81+
if span != nil {
82+
span.SetAttributes(attribute.Bool("http.request_body_compressed", true))
83+
}
84+
85+
unCompressedBody, err := io.ReadAll(req.Body)
86+
unCompressedContentLength := req.ContentLength
87+
if err != nil {
88+
if span != nil {
89+
span.RecordError(err)
90+
}
91+
return nil, err
92+
}
93+
94+
compressedBody, size, err := compressReqBody(unCompressedBody)
95+
if err != nil {
96+
if span != nil {
97+
span.RecordError(err)
98+
}
99+
return nil, err
100+
}
101+
102+
req.Header.Set("Content-Encoding", "gzip")
103+
req.Body = compressedBody
104+
req.ContentLength = int64(size)
105+
106+
if span != nil {
107+
span.SetAttributes(httpRequestBodySizeAttribute.Int(int(req.ContentLength)))
108+
}
109+
110+
// Sending request with compressed body
111+
resp, err := pipeline.Next(req, middlewareIndex)
112+
if err != nil {
113+
return nil, err
114+
}
115+
116+
// If response has status 415 retry request with uncompressed body
117+
if resp.StatusCode == 415 {
118+
delete(req.Header, "Content-Encoding")
119+
req.Body = io.NopCloser(bytes.NewBuffer(unCompressedBody))
120+
req.ContentLength = unCompressedContentLength
121+
122+
if span != nil {
123+
span.SetAttributes(httpRequestBodySizeAttribute.Int(int(req.ContentLength)),
124+
httpResponseStatusCodeAttribute.Int(415))
125+
}
126+
127+
return pipeline.Next(req, middlewareIndex)
128+
}
129+
130+
return resp, nil
131+
}
132+
133+
func contentRangeBytesIsPresent(header http.Header) bool {
134+
contentRanges, _ := header["Content-Range"]
135+
for _, contentRange := range contentRanges {
136+
if strings.Contains(strings.ToLower(contentRange), "bytes") {
137+
return true
138+
}
139+
}
140+
return false
141+
}
142+
143+
func contentEncodingIsPresent(header http.Header) bool {
144+
_, ok := header["Content-Encoding"]
145+
return ok
146+
}
147+
148+
func compressReqBody(reqBody []byte) (io.ReadSeekCloser, int, error) {
149+
var buffer bytes.Buffer
150+
gzipWriter := gzip.NewWriter(&buffer)
151+
if _, err := gzipWriter.Write(reqBody); err != nil {
152+
return nil, 0, err
153+
}
154+
155+
if err := gzipWriter.Close(); err != nil {
156+
return nil, 0, err
157+
}
158+
159+
reader := bytes.NewReader(buffer.Bytes())
160+
return NopCloser(reader), buffer.Len(), nil
161+
}

0 commit comments

Comments
 (0)