...

Source file src/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/httpconv_test.go

Documentation: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil

     1  // Code created by gotmpl. DO NOT MODIFY.
     2  // source: internal/shared/semconvutil/httpconv_test.go.tmpl
     3  
     4  // Copyright The OpenTelemetry Authors
     5  // SPDX-License-Identifier: Apache-2.0
     6  
     7  package semconvutil
     8  
     9  import (
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"net/url"
    13  	"strconv"
    14  	"testing"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"go.opentelemetry.io/otel/attribute"
    20  	"go.opentelemetry.io/otel/codes"
    21  )
    22  
    23  func TestHTTPClientResponse(t *testing.T) {
    24  	const stat, n = 201, 397
    25  	resp := &http.Response{
    26  		StatusCode:    stat,
    27  		ContentLength: n,
    28  	}
    29  	got := HTTPClientResponse(resp)
    30  	assert.Equal(t, 2, cap(got), "slice capacity")
    31  	assert.ElementsMatch(t, []attribute.KeyValue{
    32  		attribute.Key("http.status_code").Int(stat),
    33  		attribute.Key("http.response_content_length").Int(n),
    34  	}, got)
    35  }
    36  
    37  func TestHTTPSClientRequest(t *testing.T) {
    38  	req := &http.Request{
    39  		Method: http.MethodGet,
    40  		URL: &url.URL{
    41  			Scheme: "https",
    42  			Host:   "127.0.0.1:443",
    43  			Path:   "/resource",
    44  		},
    45  		Proto:      "HTTP/1.0",
    46  		ProtoMajor: 1,
    47  		ProtoMinor: 0,
    48  	}
    49  
    50  	assert.ElementsMatch(
    51  		t,
    52  		[]attribute.KeyValue{
    53  			attribute.String("http.method", "GET"),
    54  			attribute.String("http.url", "https://127.0.0.1:443/resource"),
    55  			attribute.String("net.peer.name", "127.0.0.1"),
    56  		},
    57  		HTTPClientRequest(req),
    58  	)
    59  }
    60  
    61  func TestHTTPSClientRequestMetrics(t *testing.T) {
    62  	req := &http.Request{
    63  		Method: http.MethodGet,
    64  		URL: &url.URL{
    65  			Scheme: "https",
    66  			Host:   "127.0.0.1:443",
    67  			Path:   "/resource",
    68  		},
    69  		Proto:      "HTTP/1.0",
    70  		ProtoMajor: 1,
    71  		ProtoMinor: 0,
    72  	}
    73  
    74  	assert.ElementsMatch(
    75  		t,
    76  		[]attribute.KeyValue{
    77  			attribute.String("http.method", "GET"),
    78  			attribute.String("net.peer.name", "127.0.0.1"),
    79  		},
    80  		HTTPClientRequestMetrics(req),
    81  	)
    82  }
    83  
    84  func TestHTTPClientRequest(t *testing.T) {
    85  	const (
    86  		user  = "alice"
    87  		n     = 128
    88  		agent = "Go-http-client/1.1"
    89  	)
    90  	req := &http.Request{
    91  		Method: http.MethodGet,
    92  		URL: &url.URL{
    93  			Scheme: "http",
    94  			Host:   "127.0.0.1:8080",
    95  			Path:   "/resource",
    96  		},
    97  		Proto:      "HTTP/1.0",
    98  		ProtoMajor: 1,
    99  		ProtoMinor: 0,
   100  		Header: http.Header{
   101  			"User-Agent": []string{agent},
   102  		},
   103  		ContentLength: n,
   104  	}
   105  	req.SetBasicAuth(user, "pswrd")
   106  
   107  	assert.ElementsMatch(
   108  		t,
   109  		[]attribute.KeyValue{
   110  			attribute.String("http.method", "GET"),
   111  			attribute.String("http.url", "http://127.0.0.1:8080/resource"),
   112  			attribute.String("net.peer.name", "127.0.0.1"),
   113  			attribute.Int("net.peer.port", 8080),
   114  			attribute.String("user_agent.original", agent),
   115  			attribute.Int("http.request_content_length", n),
   116  		},
   117  		HTTPClientRequest(req),
   118  	)
   119  }
   120  
   121  func TestHTTPClientRequestMetrics(t *testing.T) {
   122  	const (
   123  		user  = "alice"
   124  		n     = 128
   125  		agent = "Go-http-client/1.1"
   126  	)
   127  	req := &http.Request{
   128  		Method: http.MethodGet,
   129  		URL: &url.URL{
   130  			Scheme: "http",
   131  			Host:   "127.0.0.1:8080",
   132  			Path:   "/resource",
   133  		},
   134  		Proto:      "HTTP/1.0",
   135  		ProtoMajor: 1,
   136  		ProtoMinor: 0,
   137  		Header: http.Header{
   138  			"User-Agent": []string{agent},
   139  		},
   140  		ContentLength: n,
   141  	}
   142  	req.SetBasicAuth(user, "pswrd")
   143  
   144  	assert.ElementsMatch(
   145  		t,
   146  		[]attribute.KeyValue{
   147  			attribute.String("http.method", "GET"),
   148  			attribute.String("net.peer.name", "127.0.0.1"),
   149  			attribute.Int("net.peer.port", 8080),
   150  		},
   151  		HTTPClientRequestMetrics(req),
   152  	)
   153  }
   154  
   155  func TestHTTPClientRequestRequired(t *testing.T) {
   156  	req := new(http.Request)
   157  	var got []attribute.KeyValue
   158  	assert.NotPanics(t, func() { got = HTTPClientRequest(req) })
   159  	want := []attribute.KeyValue{
   160  		attribute.String("http.method", "GET"),
   161  		attribute.String("http.url", ""),
   162  		attribute.String("net.peer.name", ""),
   163  	}
   164  	assert.Equal(t, want, got)
   165  }
   166  
   167  func TestHTTPServerRequest(t *testing.T) {
   168  	got := make(chan *http.Request, 1)
   169  	handler := func(w http.ResponseWriter, r *http.Request) {
   170  		got <- r
   171  		w.WriteHeader(http.StatusOK)
   172  	}
   173  
   174  	srv := httptest.NewServer(http.HandlerFunc(handler))
   175  	defer srv.Close()
   176  
   177  	srvURL, err := url.Parse(srv.URL)
   178  	require.NoError(t, err)
   179  	srvPort, err := strconv.ParseInt(srvURL.Port(), 10, 32)
   180  	require.NoError(t, err)
   181  
   182  	resp, err := srv.Client().Get(srv.URL)
   183  	require.NoError(t, err)
   184  	require.NoError(t, resp.Body.Close())
   185  
   186  	req := <-got
   187  	peer, peerPort := splitHostPort(req.RemoteAddr)
   188  
   189  	const user = "alice"
   190  	req.SetBasicAuth(user, "pswrd")
   191  
   192  	const clientIP = "127.0.0.5"
   193  	req.Header.Add("X-Forwarded-For", clientIP)
   194  
   195  	assert.ElementsMatch(t,
   196  		[]attribute.KeyValue{
   197  			attribute.String("http.method", "GET"),
   198  			attribute.String("http.scheme", "http"),
   199  			attribute.String("net.host.name", srvURL.Hostname()),
   200  			attribute.Int("net.host.port", int(srvPort)),
   201  			attribute.String("net.sock.peer.addr", peer),
   202  			attribute.Int("net.sock.peer.port", peerPort),
   203  			attribute.String("user_agent.original", "Go-http-client/1.1"),
   204  			attribute.String("http.client_ip", clientIP),
   205  			attribute.String("net.protocol.version", "1.1"),
   206  			attribute.String("http.target", "/"),
   207  		},
   208  		HTTPServerRequest("", req))
   209  }
   210  
   211  func TestHTTPServerRequestMetrics(t *testing.T) {
   212  	got := make(chan *http.Request, 1)
   213  	handler := func(w http.ResponseWriter, r *http.Request) {
   214  		got <- r
   215  		w.WriteHeader(http.StatusOK)
   216  	}
   217  
   218  	srv := httptest.NewServer(http.HandlerFunc(handler))
   219  	defer srv.Close()
   220  
   221  	srvURL, err := url.Parse(srv.URL)
   222  	require.NoError(t, err)
   223  	srvPort, err := strconv.ParseInt(srvURL.Port(), 10, 32)
   224  	require.NoError(t, err)
   225  
   226  	resp, err := srv.Client().Get(srv.URL)
   227  	require.NoError(t, err)
   228  	require.NoError(t, resp.Body.Close())
   229  
   230  	req := <-got
   231  
   232  	assert.ElementsMatch(t,
   233  		[]attribute.KeyValue{
   234  			attribute.String("http.method", "GET"),
   235  			attribute.String("http.scheme", "http"),
   236  			attribute.String("net.host.name", srvURL.Hostname()),
   237  			attribute.Int("net.host.port", int(srvPort)),
   238  			attribute.String("net.protocol.name", "http"),
   239  			attribute.String("net.protocol.version", "1.1"),
   240  		},
   241  		HTTPServerRequestMetrics("", req))
   242  }
   243  
   244  func TestHTTPServerName(t *testing.T) {
   245  	req := new(http.Request)
   246  	var got []attribute.KeyValue
   247  	const (
   248  		host = "test.semconv.server"
   249  		port = 8080
   250  	)
   251  	portStr := strconv.Itoa(port)
   252  	server := host + ":" + portStr
   253  	assert.NotPanics(t, func() { got = HTTPServerRequest(server, req) })
   254  	assert.Contains(t, got, attribute.String("net.host.name", host))
   255  	assert.Contains(t, got, attribute.Int("net.host.port", port))
   256  
   257  	req = &http.Request{Host: "alt.host.name:" + portStr}
   258  	// The server parameter does not include a port, ServerRequest should use
   259  	// the port in the request Host field.
   260  	assert.NotPanics(t, func() { got = HTTPServerRequest(host, req) })
   261  	assert.Contains(t, got, attribute.String("net.host.name", host))
   262  	assert.Contains(t, got, attribute.Int("net.host.port", port))
   263  }
   264  
   265  func TestHTTPServerRequestFailsGracefully(t *testing.T) {
   266  	req := new(http.Request)
   267  	var got []attribute.KeyValue
   268  	assert.NotPanics(t, func() { got = HTTPServerRequest("", req) })
   269  	want := []attribute.KeyValue{
   270  		attribute.String("http.method", "GET"),
   271  		attribute.String("http.scheme", "http"),
   272  		attribute.String("net.host.name", ""),
   273  	}
   274  	assert.ElementsMatch(t, want, got)
   275  }
   276  
   277  func TestHTTPMethod(t *testing.T) {
   278  	assert.Equal(t, attribute.String("http.method", "POST"), hc.method("POST"))
   279  	assert.Equal(t, attribute.String("http.method", "GET"), hc.method(""))
   280  	assert.Equal(t, attribute.String("http.method", "garbage"), hc.method("garbage"))
   281  }
   282  
   283  func TestHTTPScheme(t *testing.T) {
   284  	assert.Equal(t, attribute.String("http.scheme", "http"), hc.scheme(false))
   285  	assert.Equal(t, attribute.String("http.scheme", "https"), hc.scheme(true))
   286  }
   287  
   288  func TestHTTPServerClientIP(t *testing.T) {
   289  	tests := []struct {
   290  		xForwardedFor string
   291  		want          string
   292  	}{
   293  		{"", ""},
   294  		{"127.0.0.1", "127.0.0.1"},
   295  		{"127.0.0.1,127.0.0.5", "127.0.0.1"},
   296  	}
   297  	for _, test := range tests {
   298  		got := serverClientIP(test.xForwardedFor)
   299  		assert.Equal(t, test.want, got, test.xForwardedFor)
   300  	}
   301  }
   302  
   303  func TestRequiredHTTPPort(t *testing.T) {
   304  	tests := []struct {
   305  		https bool
   306  		port  int
   307  		want  int
   308  	}{
   309  		{true, 443, -1},
   310  		{true, 80, 80},
   311  		{true, 8081, 8081},
   312  		{false, 443, 443},
   313  		{false, 80, -1},
   314  		{false, 8080, 8080},
   315  	}
   316  	for _, test := range tests {
   317  		got := requiredHTTPPort(test.https, test.port)
   318  		assert.Equal(t, test.want, got, test.https, test.port)
   319  	}
   320  }
   321  
   322  func TestFirstHostPort(t *testing.T) {
   323  	host, port := "127.0.0.1", 8080
   324  	hostport := "127.0.0.1:8080"
   325  	sources := [][]string{
   326  		{hostport},
   327  		{"", hostport},
   328  		{"", "", hostport},
   329  		{"", "", hostport, ""},
   330  		{"", "", hostport, "127.0.0.3:80"},
   331  	}
   332  
   333  	for _, src := range sources {
   334  		h, p := firstHostPort(src...)
   335  		assert.Equal(t, host, h, src)
   336  		assert.Equal(t, port, p, src)
   337  	}
   338  }
   339  
   340  func TestHTTPClientStatus(t *testing.T) {
   341  	tests := []struct {
   342  		code int
   343  		stat codes.Code
   344  		msg  bool
   345  	}{
   346  		{0, codes.Error, true},
   347  		{http.StatusContinue, codes.Unset, false},
   348  		{http.StatusSwitchingProtocols, codes.Unset, false},
   349  		{http.StatusProcessing, codes.Unset, false},
   350  		{http.StatusEarlyHints, codes.Unset, false},
   351  		{http.StatusOK, codes.Unset, false},
   352  		{http.StatusCreated, codes.Unset, false},
   353  		{http.StatusAccepted, codes.Unset, false},
   354  		{http.StatusNonAuthoritativeInfo, codes.Unset, false},
   355  		{http.StatusNoContent, codes.Unset, false},
   356  		{http.StatusResetContent, codes.Unset, false},
   357  		{http.StatusPartialContent, codes.Unset, false},
   358  		{http.StatusMultiStatus, codes.Unset, false},
   359  		{http.StatusAlreadyReported, codes.Unset, false},
   360  		{http.StatusIMUsed, codes.Unset, false},
   361  		{http.StatusMultipleChoices, codes.Unset, false},
   362  		{http.StatusMovedPermanently, codes.Unset, false},
   363  		{http.StatusFound, codes.Unset, false},
   364  		{http.StatusSeeOther, codes.Unset, false},
   365  		{http.StatusNotModified, codes.Unset, false},
   366  		{http.StatusUseProxy, codes.Unset, false},
   367  		{306, codes.Unset, false},
   368  		{http.StatusTemporaryRedirect, codes.Unset, false},
   369  		{http.StatusPermanentRedirect, codes.Unset, false},
   370  		{http.StatusBadRequest, codes.Error, false},
   371  		{http.StatusUnauthorized, codes.Error, false},
   372  		{http.StatusPaymentRequired, codes.Error, false},
   373  		{http.StatusForbidden, codes.Error, false},
   374  		{http.StatusNotFound, codes.Error, false},
   375  		{http.StatusMethodNotAllowed, codes.Error, false},
   376  		{http.StatusNotAcceptable, codes.Error, false},
   377  		{http.StatusProxyAuthRequired, codes.Error, false},
   378  		{http.StatusRequestTimeout, codes.Error, false},
   379  		{http.StatusConflict, codes.Error, false},
   380  		{http.StatusGone, codes.Error, false},
   381  		{http.StatusLengthRequired, codes.Error, false},
   382  		{http.StatusPreconditionFailed, codes.Error, false},
   383  		{http.StatusRequestEntityTooLarge, codes.Error, false},
   384  		{http.StatusRequestURITooLong, codes.Error, false},
   385  		{http.StatusUnsupportedMediaType, codes.Error, false},
   386  		{http.StatusRequestedRangeNotSatisfiable, codes.Error, false},
   387  		{http.StatusExpectationFailed, codes.Error, false},
   388  		{http.StatusTeapot, codes.Error, false},
   389  		{http.StatusMisdirectedRequest, codes.Error, false},
   390  		{http.StatusUnprocessableEntity, codes.Error, false},
   391  		{http.StatusLocked, codes.Error, false},
   392  		{http.StatusFailedDependency, codes.Error, false},
   393  		{http.StatusTooEarly, codes.Error, false},
   394  		{http.StatusUpgradeRequired, codes.Error, false},
   395  		{http.StatusPreconditionRequired, codes.Error, false},
   396  		{http.StatusTooManyRequests, codes.Error, false},
   397  		{http.StatusRequestHeaderFieldsTooLarge, codes.Error, false},
   398  		{http.StatusUnavailableForLegalReasons, codes.Error, false},
   399  		{499, codes.Error, false},
   400  		{http.StatusInternalServerError, codes.Error, false},
   401  		{http.StatusNotImplemented, codes.Error, false},
   402  		{http.StatusBadGateway, codes.Error, false},
   403  		{http.StatusServiceUnavailable, codes.Error, false},
   404  		{http.StatusGatewayTimeout, codes.Error, false},
   405  		{http.StatusHTTPVersionNotSupported, codes.Error, false},
   406  		{http.StatusVariantAlsoNegotiates, codes.Error, false},
   407  		{http.StatusInsufficientStorage, codes.Error, false},
   408  		{http.StatusLoopDetected, codes.Error, false},
   409  		{http.StatusNotExtended, codes.Error, false},
   410  		{http.StatusNetworkAuthenticationRequired, codes.Error, false},
   411  		{600, codes.Error, true},
   412  	}
   413  
   414  	for _, test := range tests {
   415  		t.Run(strconv.Itoa(test.code), func(t *testing.T) {
   416  			c, msg := HTTPClientStatus(test.code)
   417  			assert.Equal(t, test.stat, c)
   418  			if test.msg && msg == "" {
   419  				t.Errorf("expected non-empty message for %d", test.code)
   420  			} else if !test.msg && msg != "" {
   421  				t.Errorf("expected empty message for %d, got: %s", test.code, msg)
   422  			}
   423  		})
   424  	}
   425  }
   426  
   427  func TestHTTPServerStatus(t *testing.T) {
   428  	tests := []struct {
   429  		code int
   430  		stat codes.Code
   431  		msg  bool
   432  	}{
   433  		{0, codes.Error, true},
   434  		{http.StatusContinue, codes.Unset, false},
   435  		{http.StatusSwitchingProtocols, codes.Unset, false},
   436  		{http.StatusProcessing, codes.Unset, false},
   437  		{http.StatusEarlyHints, codes.Unset, false},
   438  		{http.StatusOK, codes.Unset, false},
   439  		{http.StatusCreated, codes.Unset, false},
   440  		{http.StatusAccepted, codes.Unset, false},
   441  		{http.StatusNonAuthoritativeInfo, codes.Unset, false},
   442  		{http.StatusNoContent, codes.Unset, false},
   443  		{http.StatusResetContent, codes.Unset, false},
   444  		{http.StatusPartialContent, codes.Unset, false},
   445  		{http.StatusMultiStatus, codes.Unset, false},
   446  		{http.StatusAlreadyReported, codes.Unset, false},
   447  		{http.StatusIMUsed, codes.Unset, false},
   448  		{http.StatusMultipleChoices, codes.Unset, false},
   449  		{http.StatusMovedPermanently, codes.Unset, false},
   450  		{http.StatusFound, codes.Unset, false},
   451  		{http.StatusSeeOther, codes.Unset, false},
   452  		{http.StatusNotModified, codes.Unset, false},
   453  		{http.StatusUseProxy, codes.Unset, false},
   454  		{306, codes.Unset, false},
   455  		{http.StatusTemporaryRedirect, codes.Unset, false},
   456  		{http.StatusPermanentRedirect, codes.Unset, false},
   457  		{http.StatusBadRequest, codes.Unset, false},
   458  		{http.StatusUnauthorized, codes.Unset, false},
   459  		{http.StatusPaymentRequired, codes.Unset, false},
   460  		{http.StatusForbidden, codes.Unset, false},
   461  		{http.StatusNotFound, codes.Unset, false},
   462  		{http.StatusMethodNotAllowed, codes.Unset, false},
   463  		{http.StatusNotAcceptable, codes.Unset, false},
   464  		{http.StatusProxyAuthRequired, codes.Unset, false},
   465  		{http.StatusRequestTimeout, codes.Unset, false},
   466  		{http.StatusConflict, codes.Unset, false},
   467  		{http.StatusGone, codes.Unset, false},
   468  		{http.StatusLengthRequired, codes.Unset, false},
   469  		{http.StatusPreconditionFailed, codes.Unset, false},
   470  		{http.StatusRequestEntityTooLarge, codes.Unset, false},
   471  		{http.StatusRequestURITooLong, codes.Unset, false},
   472  		{http.StatusUnsupportedMediaType, codes.Unset, false},
   473  		{http.StatusRequestedRangeNotSatisfiable, codes.Unset, false},
   474  		{http.StatusExpectationFailed, codes.Unset, false},
   475  		{http.StatusTeapot, codes.Unset, false},
   476  		{http.StatusMisdirectedRequest, codes.Unset, false},
   477  		{http.StatusUnprocessableEntity, codes.Unset, false},
   478  		{http.StatusLocked, codes.Unset, false},
   479  		{http.StatusFailedDependency, codes.Unset, false},
   480  		{http.StatusTooEarly, codes.Unset, false},
   481  		{http.StatusUpgradeRequired, codes.Unset, false},
   482  		{http.StatusPreconditionRequired, codes.Unset, false},
   483  		{http.StatusTooManyRequests, codes.Unset, false},
   484  		{http.StatusRequestHeaderFieldsTooLarge, codes.Unset, false},
   485  		{http.StatusUnavailableForLegalReasons, codes.Unset, false},
   486  		{499, codes.Unset, false},
   487  		{http.StatusInternalServerError, codes.Error, false},
   488  		{http.StatusNotImplemented, codes.Error, false},
   489  		{http.StatusBadGateway, codes.Error, false},
   490  		{http.StatusServiceUnavailable, codes.Error, false},
   491  		{http.StatusGatewayTimeout, codes.Error, false},
   492  		{http.StatusHTTPVersionNotSupported, codes.Error, false},
   493  		{http.StatusVariantAlsoNegotiates, codes.Error, false},
   494  		{http.StatusInsufficientStorage, codes.Error, false},
   495  		{http.StatusLoopDetected, codes.Error, false},
   496  		{http.StatusNotExtended, codes.Error, false},
   497  		{http.StatusNetworkAuthenticationRequired, codes.Error, false},
   498  		{600, codes.Error, true},
   499  	}
   500  
   501  	for _, test := range tests {
   502  		c, msg := HTTPServerStatus(test.code)
   503  		assert.Equal(t, test.stat, c)
   504  		if test.msg && msg == "" {
   505  			t.Errorf("expected non-empty message for %d", test.code)
   506  		} else if !test.msg && msg != "" {
   507  			t.Errorf("expected empty message for %d, got: %s", test.code, msg)
   508  		}
   509  	}
   510  }
   511  

View as plain text