=== added file '.travis.yml'
--- old/.travis.yml	1970-01-01 00:00:00 +0000
+++ new/.travis.yml	2021-04-09 12:19:08 +0000
@@ -0,0 +1,14 @@
+language: go
+
+go:
+  - 1.13.x
+  - 1.14.x
+  - tip
+
+arch:
+  - amd64
+  - ppc64le
+
+script:
+  - go test -v -cover -race ./...
+

=== added file 'LICENSE'
--- old/LICENSE	1970-01-01 00:00:00 +0000
+++ new/LICENSE	2018-12-13 18:09:13 +0000
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

=== added file 'README.md'
--- old/README.md	1970-01-01 00:00:00 +0000
+++ new/README.md	2018-12-13 18:11:19 +0000
@@ -0,0 +1,22 @@
+# go-stdlib
+
+This repository contains OpenTracing instrumentation for packages in
+the Go standard library.
+
+For documentation on the packages,
+[check godoc](https://godoc.org/github.com/opentracing-contrib/go-stdlib/).
+
+**The APIs in the various packages are experimental and may change in
+the future. You should vendor them to avoid spurious breakage.**
+
+## Packages
+
+Instrumentation is provided for the following packages, with the
+following caveats:
+
+- **net/http**: Client and server instrumentation. *Only supported
+  with Go 1.7 and later.*
+
+## License
+
+By contributing to this repository, you agree that your contributions will be licensed under [Apache 2.0 License](./LICENSE).

=== added file 'go.mod'
--- old/go.mod	1970-01-01 00:00:00 +0000
+++ new/go.mod	2020-06-07 23:58:44 +0000
@@ -0,0 +1,7 @@
+module github.com/opentracing-contrib/go-stdlib
+
+go 1.14
+
+require (
+	github.com/opentracing/opentracing-go v1.1.0
+)

=== added file 'go.sum'
--- old/go.sum	1970-01-01 00:00:00 +0000
+++ new/go.sum	2020-06-07 23:58:44 +0000
@@ -0,0 +1,52 @@
+github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8=
+github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
+github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
+github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/go-delve/delve v1.4.1 h1:kZs0umEv+VKnK84kY9/ZXWrakdLTeRTyYjFdgLelZCQ=
+github.com/go-delve/delve v1.4.1/go.mod h1:vmy6iObn7zg8FQ5KOCIe6TruMNsqpoZO8uMiRea+97k=
+github.com/google/go-dap v0.2.0 h1:whjIGQRumwbR40qRU7CEKuFLmePUUc2s4Nt9DoXXxWk=
+github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ=
+github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561 h1:isR/L+BIZ+rqODWYR/f526ygrBMGKZYFhaaFRDGvuZ8=
+github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
+github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b h1:8uaXtUkxiy+T/zdLWuxa/PG4so0TPZDZfafFNNSaptE=
+github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
+github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
+github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372 h1:eRfW1vRS4th8IX2iQeyqQ8cOUNOySvAYJ0IUvTXGoYA=
+github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1 h1:7bozMfSdo41n2NOc0GsVTTVUiA+Ncaj6pXNpm4UHKys=
+github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+go.starlark.net v0.0.0-20190702223751-32f345186213 h1:lkYv5AKwvvduv5XWP6szk/bvvgO6aDeUujhZQXIFTes=
+go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
+golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI=
+golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

=== added directory 'nethttp'
=== added file 'nethttp/client.go'
--- old/nethttp/client.go	1970-01-01 00:00:00 +0000
+++ new/nethttp/client.go	2021-04-09 12:19:08 +0000
@@ -0,0 +1,370 @@
+// +build go1.7
+
+package nethttp
+
+import (
+	"context"
+	"io"
+	"net/http"
+	"net/http/httptrace"
+	"net/url"
+
+	"github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/ext"
+	"github.com/opentracing/opentracing-go/log"
+)
+
+type contextKey int
+
+const (
+	keyTracer contextKey = iota
+)
+
+const defaultComponentName = "net/http"
+
+// Transport wraps a RoundTripper. If a request is being traced with
+// Tracer, Transport will inject the current span into the headers,
+// and set HTTP related tags on the span.
+type Transport struct {
+	// The actual RoundTripper to use for the request. A nil
+	// RoundTripper defaults to http.DefaultTransport.
+	http.RoundTripper
+}
+
+type clientOptions struct {
+	operationName            string
+	componentName            string
+	urlTagFunc               func(u *url.URL) string
+	disableClientTrace       bool
+	disableInjectSpanContext bool
+	spanObserver             func(span opentracing.Span, r *http.Request)
+}
+
+// ClientOption contols the behavior of TraceRequest.
+type ClientOption func(*clientOptions)
+
+// OperationName returns a ClientOption that sets the operation
+// name for the client-side span.
+func OperationName(operationName string) ClientOption {
+	return func(options *clientOptions) {
+		options.operationName = operationName
+	}
+}
+
+// URLTagFunc returns a ClientOption that uses given function f
+// to set the span's http.url tag. Can be used to change the default
+// http.url tag, eg to redact sensitive information.
+func URLTagFunc(f func(u *url.URL) string) ClientOption {
+	return func(options *clientOptions) {
+		options.urlTagFunc = f
+	}
+}
+
+// ComponentName returns a ClientOption that sets the component
+// name for the client-side span.
+func ComponentName(componentName string) ClientOption {
+	return func(options *clientOptions) {
+		options.componentName = componentName
+	}
+}
+
+// ClientTrace returns a ClientOption that turns on or off
+// extra instrumentation via httptrace.WithClientTrace.
+func ClientTrace(enabled bool) ClientOption {
+	return func(options *clientOptions) {
+		options.disableClientTrace = !enabled
+	}
+}
+
+// InjectSpanContext returns a ClientOption that turns on or off
+// injection of the Span context in the request HTTP headers.
+// If this option is not used, the default behaviour is to
+// inject the span context.
+func InjectSpanContext(enabled bool) ClientOption {
+	return func(options *clientOptions) {
+		options.disableInjectSpanContext = !enabled
+	}
+}
+
+// ClientSpanObserver returns a ClientOption that observes the span
+// for the client-side span.
+func ClientSpanObserver(f func(span opentracing.Span, r *http.Request)) ClientOption {
+	return func(options *clientOptions) {
+		options.spanObserver = f
+	}
+}
+
+// TraceRequest adds a ClientTracer to req, tracing the request and
+// all requests caused due to redirects. When tracing requests this
+// way you must also use Transport.
+//
+// Example:
+//
+// 	func AskGoogle(ctx context.Context) error {
+// 		client := &http.Client{Transport: &nethttp.Transport{}}
+// 		req, err := http.NewRequest("GET", "http://google.com", nil)
+// 		if err != nil {
+// 			return err
+// 		}
+// 		req = req.WithContext(ctx) // extend existing trace, if any
+//
+// 		req, ht := nethttp.TraceRequest(tracer, req)
+// 		defer ht.Finish()
+//
+// 		res, err := client.Do(req)
+// 		if err != nil {
+// 			return err
+// 		}
+// 		res.Body.Close()
+// 		return nil
+// 	}
+func TraceRequest(tr opentracing.Tracer, req *http.Request, options ...ClientOption) (*http.Request, *Tracer) {
+	opts := &clientOptions{
+		urlTagFunc: func(u *url.URL) string {
+			return u.String()
+		},
+		spanObserver: func(_ opentracing.Span, _ *http.Request) {},
+	}
+	for _, opt := range options {
+		opt(opts)
+	}
+	ht := &Tracer{tr: tr, opts: opts}
+	ctx := req.Context()
+	if !opts.disableClientTrace {
+		ctx = httptrace.WithClientTrace(ctx, ht.clientTrace())
+	}
+	req = req.WithContext(context.WithValue(ctx, keyTracer, ht))
+	return req, ht
+}
+
+type closeTracker struct {
+	io.ReadCloser
+	sp opentracing.Span
+}
+
+func (c closeTracker) Close() error {
+	err := c.ReadCloser.Close()
+	c.sp.LogFields(log.String("event", "ClosedBody"))
+	c.sp.Finish()
+	return err
+}
+
+type writerCloseTracker struct {
+	io.ReadWriteCloser
+	sp opentracing.Span
+}
+
+func (c writerCloseTracker) Close() error {
+	err := c.ReadWriteCloser.Close()
+	c.sp.LogFields(log.String("event", "ClosedBody"))
+	c.sp.Finish()
+	return err
+}
+
+// TracerFromRequest retrieves the Tracer from the request. If the request does
+// not have a Tracer it will return nil.
+func TracerFromRequest(req *http.Request) *Tracer {
+	tr, ok := req.Context().Value(keyTracer).(*Tracer)
+	if !ok {
+		return nil
+	}
+	return tr
+}
+
+// RoundTrip implements the RoundTripper interface.
+func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
+	rt := t.RoundTripper
+	if rt == nil {
+		rt = http.DefaultTransport
+	}
+	tracer := TracerFromRequest(req)
+	if tracer == nil {
+		return rt.RoundTrip(req)
+	}
+
+	tracer.start(req)
+
+	ext.HTTPMethod.Set(tracer.sp, req.Method)
+	ext.HTTPUrl.Set(tracer.sp, tracer.opts.urlTagFunc(req.URL))
+	ext.PeerAddress.Set(tracer.sp, req.URL.Host)
+	tracer.opts.spanObserver(tracer.sp, req)
+
+	if !tracer.opts.disableInjectSpanContext {
+		carrier := opentracing.HTTPHeadersCarrier(req.Header)
+		tracer.sp.Tracer().Inject(tracer.sp.Context(), opentracing.HTTPHeaders, carrier)
+	}
+
+	resp, err := rt.RoundTrip(req)
+
+	if err != nil {
+		tracer.sp.Finish()
+		return resp, err
+	}
+	ext.HTTPStatusCode.Set(tracer.sp, uint16(resp.StatusCode))
+	if resp.StatusCode >= http.StatusInternalServerError {
+		ext.Error.Set(tracer.sp, true)
+	}
+	if req.Method == "HEAD" {
+		tracer.sp.Finish()
+	} else {
+		readWriteCloser, ok := resp.Body.(io.ReadWriteCloser)
+		if ok {
+			resp.Body = writerCloseTracker{readWriteCloser, tracer.sp}
+		} else {
+			resp.Body = closeTracker{resp.Body, tracer.sp}
+		}
+	}
+	return resp, nil
+}
+
+// Tracer holds tracing details for one HTTP request.
+type Tracer struct {
+	tr   opentracing.Tracer
+	root opentracing.Span
+	sp   opentracing.Span
+	opts *clientOptions
+}
+
+func (h *Tracer) start(req *http.Request) opentracing.Span {
+	if h.root == nil {
+		parent := opentracing.SpanFromContext(req.Context())
+		var spanctx opentracing.SpanContext
+		if parent != nil {
+			spanctx = parent.Context()
+		}
+		operationName := h.opts.operationName
+		if operationName == "" {
+			operationName = "HTTP Client"
+		}
+		root := h.tr.StartSpan(operationName, opentracing.ChildOf(spanctx))
+		h.root = root
+	}
+
+	ctx := h.root.Context()
+	h.sp = h.tr.StartSpan("HTTP "+req.Method, opentracing.ChildOf(ctx), ext.SpanKindRPCClient)
+
+	componentName := h.opts.componentName
+	if componentName == "" {
+		componentName = defaultComponentName
+	}
+	ext.Component.Set(h.sp, componentName)
+
+	return h.sp
+}
+
+// Finish finishes the span of the traced request.
+func (h *Tracer) Finish() {
+	if h.root != nil {
+		h.root.Finish()
+	}
+}
+
+// Span returns the root span of the traced request. This function
+// should only be called after the request has been executed.
+func (h *Tracer) Span() opentracing.Span {
+	return h.root
+}
+
+func (h *Tracer) clientTrace() *httptrace.ClientTrace {
+	return &httptrace.ClientTrace{
+		GetConn:              h.getConn,
+		GotConn:              h.gotConn,
+		PutIdleConn:          h.putIdleConn,
+		GotFirstResponseByte: h.gotFirstResponseByte,
+		Got100Continue:       h.got100Continue,
+		DNSStart:             h.dnsStart,
+		DNSDone:              h.dnsDone,
+		ConnectStart:         h.connectStart,
+		ConnectDone:          h.connectDone,
+		WroteHeaders:         h.wroteHeaders,
+		Wait100Continue:      h.wait100Continue,
+		WroteRequest:         h.wroteRequest,
+	}
+}
+
+func (h *Tracer) getConn(hostPort string) {
+	h.sp.LogFields(log.String("event", "GetConn"), log.String("hostPort", hostPort))
+}
+
+func (h *Tracer) gotConn(info httptrace.GotConnInfo) {
+	h.sp.SetTag("net/http.reused", info.Reused)
+	h.sp.SetTag("net/http.was_idle", info.WasIdle)
+	h.sp.LogFields(log.String("event", "GotConn"))
+}
+
+func (h *Tracer) putIdleConn(error) {
+	h.sp.LogFields(log.String("event", "PutIdleConn"))
+}
+
+func (h *Tracer) gotFirstResponseByte() {
+	h.sp.LogFields(log.String("event", "GotFirstResponseByte"))
+}
+
+func (h *Tracer) got100Continue() {
+	h.sp.LogFields(log.String("event", "Got100Continue"))
+}
+
+func (h *Tracer) dnsStart(info httptrace.DNSStartInfo) {
+	h.sp.LogFields(
+		log.String("event", "DNSStart"),
+		log.String("host", info.Host),
+	)
+}
+
+func (h *Tracer) dnsDone(info httptrace.DNSDoneInfo) {
+	fields := []log.Field{log.String("event", "DNSDone")}
+	for _, addr := range info.Addrs {
+		fields = append(fields, log.String("addr", addr.String()))
+	}
+	if info.Err != nil {
+		fields = append(fields, log.Error(info.Err))
+	}
+	h.sp.LogFields(fields...)
+}
+
+func (h *Tracer) connectStart(network, addr string) {
+	h.sp.LogFields(
+		log.String("event", "ConnectStart"),
+		log.String("network", network),
+		log.String("addr", addr),
+	)
+}
+
+func (h *Tracer) connectDone(network, addr string, err error) {
+	if err != nil {
+		h.sp.LogFields(
+			log.String("message", "ConnectDone"),
+			log.String("network", network),
+			log.String("addr", addr),
+			log.String("event", "error"),
+			log.Error(err),
+		)
+	} else {
+		h.sp.LogFields(
+			log.String("event", "ConnectDone"),
+			log.String("network", network),
+			log.String("addr", addr),
+		)
+	}
+}
+
+func (h *Tracer) wroteHeaders() {
+	h.sp.LogFields(log.String("event", "WroteHeaders"))
+}
+
+func (h *Tracer) wait100Continue() {
+	h.sp.LogFields(log.String("event", "Wait100Continue"))
+}
+
+func (h *Tracer) wroteRequest(info httptrace.WroteRequestInfo) {
+	if info.Err != nil {
+		h.sp.LogFields(
+			log.String("message", "WroteRequest"),
+			log.String("event", "error"),
+			log.Error(info.Err),
+		)
+		ext.Error.Set(h.sp, true)
+	} else {
+		h.sp.LogFields(log.String("event", "WroteRequest"))
+	}
+}

=== added file 'nethttp/client_test.go'
--- old/nethttp/client_test.go	1970-01-01 00:00:00 +0000
+++ new/nethttp/client_test.go	2021-04-09 12:19:08 +0000
@@ -0,0 +1,323 @@
+package nethttp
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"testing"
+
+	opentracing "github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/ext"
+	"github.com/opentracing/opentracing-go/mocktracer"
+)
+
+func makeRequest(t *testing.T, url string, options ...ClientOption) []*mocktracer.MockSpan {
+	tr := &mocktracer.MockTracer{}
+	span := tr.StartSpan("toplevel")
+	client := &http.Client{Transport: &Transport{}}
+	req, err := http.NewRequest("GET", url, nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span))
+	req, ht := TraceRequest(tr, req, options...)
+	resp, err := client.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+	_ = resp.Body.Close()
+	ht.Finish()
+	span.Finish()
+
+	return tr.FinishedSpans()
+}
+
+func TestClientTrace(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/ok", func(w http.ResponseWriter, r *http.Request) {})
+	mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
+		http.Redirect(w, r, "/ok", http.StatusTemporaryRedirect)
+	})
+	mux.HandleFunc("/fail", func(w http.ResponseWriter, r *http.Request) {
+		http.Error(w, "failure", http.StatusInternalServerError)
+	})
+	srv := httptest.NewServer(mux)
+	defer srv.Close()
+
+	helloWorldObserver := func(s opentracing.Span, r *http.Request) {
+		s.SetTag("hello", "world")
+	}
+
+	tests := []struct {
+		url          string
+		num          int
+		opts         []ClientOption
+		opName       string
+		expectedTags map[string]interface{}
+	}{
+		{url: "/ok", num: 3, opts: nil, opName: "HTTP Client"},
+		{url: "/redirect", num: 4, opts: []ClientOption{OperationName("client-span")}, opName: "client-span"},
+		{url: "/fail", num: 3, opts: nil, opName: "HTTP Client", expectedTags: makeTags(string(ext.Error), true)},
+		{url: "/ok", num: 3, opts: []ClientOption{ClientSpanObserver(helloWorldObserver)}, opName: "HTTP Client", expectedTags: makeTags("hello", "world")},
+	}
+
+	for _, tt := range tests {
+		t.Log(tt.opName)
+		spans := makeRequest(t, srv.URL+tt.url, tt.opts...)
+		if got, want := len(spans), tt.num; got != want {
+			t.Fatalf("got %d spans, expected %d", got, want)
+		}
+		var rootSpan *mocktracer.MockSpan
+		for _, span := range spans {
+			if span.ParentID == 0 {
+				rootSpan = span
+				break
+			}
+		}
+		if rootSpan == nil {
+			t.Fatal("cannot find root span with ParentID==0")
+		}
+
+		foundClientSpan := false
+		for _, span := range spans {
+			if span.ParentID == rootSpan.SpanContext.SpanID {
+				foundClientSpan = true
+				if got, want := span.OperationName, tt.opName; got != want {
+					t.Fatalf("got %s operation name, expected %s", got, want)
+				}
+			}
+			if span.OperationName == "HTTP GET" {
+				logs := span.Logs()
+				if len(logs) < 6 {
+					t.Fatalf("got %d, expected at least %d log events", len(logs), 6)
+				}
+
+				key := logs[0].Fields[0].Key
+				if key != "event" {
+					t.Fatalf("got %s, expected %s", key, "event")
+				}
+				v := logs[0].Fields[0].ValueString
+				if v != "GetConn" {
+					t.Fatalf("got %s, expected %s", v, "GetConn")
+				}
+
+				for k, expected := range tt.expectedTags {
+					result := span.Tag(k)
+					if expected != result {
+						t.Fatalf("got %v, expected %v, for key %s", result, expected, k)
+					}
+				}
+			}
+		}
+		if !foundClientSpan {
+			t.Fatal("cannot find client span")
+		}
+	}
+}
+
+func TestTracerFromRequest(t *testing.T) {
+	req, err := http.NewRequest("GET", "foobar", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ht := TracerFromRequest(req)
+	if ht != nil {
+		t.Fatal("request should not have a tracer yet")
+	}
+
+	tr := &mocktracer.MockTracer{}
+	req, expected := TraceRequest(tr, req)
+
+	ht = TracerFromRequest(req)
+	if ht != expected {
+		t.Fatalf("got %v, expected %v", ht, expected)
+	}
+}
+
+func TestWriteCloserFromRequest(t *testing.T) {
+	wait := make(chan bool, 0)
+	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer func() {
+			wait <- true
+		}()
+
+		w.Header().Set("Upgrade", "websocket")
+		w.Header().Set("Connection", "Upgrade")
+		w.WriteHeader(http.StatusSwitchingProtocols)
+
+		hijacker := w.(http.Hijacker)
+		_, rw, err := hijacker.Hijack()
+
+		if err != nil {
+			t.Fatal("Failed to hijack connection")
+		}
+
+		line, _, err := rw.ReadLine()
+		if string(line) != "ping" {
+			t.Fatalf("Expected 'ping' received %q", string(line))
+		}
+
+		if err != nil {
+			t.Fatal(err)
+		}
+	}))
+
+	var buf bytes.Buffer
+	req, err := http.NewRequest("POST", srv.URL, &buf)
+	req.Header.Set("Connection", "upgrade")
+	req.Header.Set("Upgrade", "websocket")
+	req.Proto = "HTTP/1.1"
+	req.ProtoMajor = 1
+	req.ProtoMinor = 1
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	tr := &mocktracer.MockTracer{}
+	req, _ = TraceRequest(tr, req)
+
+	client := &http.Client{Transport: &Transport{}}
+	resp, err := client.Do(req)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rw, ok := resp.Body.(io.ReadWriteCloser)
+	if !ok {
+		t.Fatal("resp.Body is not a io.ReadWriteCloser")
+	}
+
+	fmt.Fprint(rw, "ping\n")
+	<-wait
+	rw.Close()
+}
+
+func TestInjectSpanContext(t *testing.T) {
+	tests := []struct {
+		name                     string
+		expectContextPropagation bool
+		opts                     []ClientOption
+	}{
+		{name: "Default", expectContextPropagation: true, opts: nil},
+		{name: "True", expectContextPropagation: true, opts: []ClientOption{InjectSpanContext(true)}},
+		{name: "False", expectContextPropagation: false, opts: []ClientOption{InjectSpanContext(false)}},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var handlerCalled bool
+			srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+				handlerCalled = true
+				srvTr := mocktracer.New()
+				ctx, err := srvTr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
+
+				if err != nil && tt.expectContextPropagation {
+					t.Fatal(err)
+				}
+
+				if tt.expectContextPropagation {
+					if err != nil || ctx == nil {
+						t.Fatal("expected propagation but unable to extract")
+					}
+				} else {
+					// Expect "opentracing: SpanContext not found in Extract carrier" when not injected
+					// Can't check ctx directly, because it gets set to emptyContext
+					if err == nil {
+						t.Fatal("unexpected propagation")
+					}
+				}
+			}))
+
+			tr := mocktracer.New()
+			span := tr.StartSpan("root")
+
+			req, err := http.NewRequest("GET", srv.URL, nil)
+			if err != nil {
+				t.Fatal(err)
+			}
+			req = req.WithContext(opentracing.ContextWithSpan(req.Context(), span))
+
+			req, ht := TraceRequest(tr, req, tt.opts...)
+
+			client := &http.Client{Transport: &Transport{}}
+			resp, err := client.Do(req)
+			if err != nil {
+				t.Fatal(err)
+			}
+			_ = resp.Body.Close()
+
+			ht.Finish()
+			span.Finish()
+
+			srv.Close()
+
+			if !handlerCalled {
+				t.Fatal("server handler never called")
+			}
+		})
+	}
+}
+
+func makeTags(keyVals ...interface{}) map[string]interface{} {
+	result := make(map[string]interface{}, len(keyVals)/2)
+	for i := 0; i < len(keyVals)-1; i += 2 {
+		key := keyVals[i].(string)
+		result[key] = keyVals[i+1]
+	}
+	return result
+}
+
+func TestClientCustomURL(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/ok", func(w http.ResponseWriter, r *http.Request) {})
+	srv := httptest.NewServer(mux)
+	defer srv.Close()
+
+	fn := func(u *url.URL) string {
+		// Simulate redacting token
+		return srv.URL + u.Path + "?token=*"
+	}
+
+	tests := []struct {
+		opts []ClientOption
+		url  string
+		tag  string
+	}{
+		// These first cases fail early
+		{[]ClientOption{}, "/ok?token=a", srv.URL + "/ok?token=a"},
+		{[]ClientOption{URLTagFunc(fn)}, "/ok?token=c", srv.URL + "/ok?token=*"},
+		// Disable ClientTrace to fire RoundTrip
+		{[]ClientOption{ClientTrace(false)}, "/ok?token=b", srv.URL + "/ok?token=b"},
+		{[]ClientOption{ClientTrace(false), URLTagFunc(fn)}, "/ok?token=c", srv.URL + "/ok?token=*"},
+	}
+
+	for _, tt := range tests {
+		var clientSpan *mocktracer.MockSpan
+
+		spans := makeRequest(t, srv.URL+tt.url, tt.opts...)
+		for _, span := range spans {
+			if span.OperationName == "HTTP GET" {
+				clientSpan = span
+				break
+			}
+		}
+		if clientSpan == nil {
+			t.Fatal("cannot find client span")
+		}
+		tag := clientSpan.Tags()["http.url"]
+		if got, want := tag, tt.tag; got != want {
+			t.Fatalf("got %s tag name, expected %s", got, want)
+		}
+		peerAddress, ok := clientSpan.Tags()["peer.address"]
+		if !ok {
+			t.Fatal("cannot find peer.address tag")
+		}
+		if peerAddress != srv.Listener.Addr().String() {
+			t.Fatalf("got %s want %s in peer.address tag", peerAddress, srv.Listener.Addr().String())
+		}
+	}
+}

=== added file 'nethttp/doc.go'
--- old/nethttp/doc.go	1970-01-01 00:00:00 +0000
+++ new/nethttp/doc.go	2016-07-20 22:20:56 +0000
@@ -0,0 +1,3 @@
+// Package nethttp provides OpenTracing instrumentation for the
+// net/http package.
+package nethttp

=== added file 'nethttp/server.go'
--- old/nethttp/server.go	1970-01-01 00:00:00 +0000
+++ new/nethttp/server.go	2020-06-08 03:54:42 +0000
@@ -0,0 +1,157 @@
+// +build go1.7
+
+package nethttp
+
+import (
+	"net/http"
+	"net/url"
+
+	opentracing "github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/ext"
+)
+
+type mwOptions struct {
+	opNameFunc    func(r *http.Request) string
+	spanFilter    func(r *http.Request) bool
+	spanObserver  func(span opentracing.Span, r *http.Request)
+	urlTagFunc    func(u *url.URL) string
+	componentName string
+}
+
+// MWOption controls the behavior of the Middleware.
+type MWOption func(*mwOptions)
+
+// OperationNameFunc returns a MWOption that uses given function f
+// to generate operation name for each server-side span.
+func OperationNameFunc(f func(r *http.Request) string) MWOption {
+	return func(options *mwOptions) {
+		options.opNameFunc = f
+	}
+}
+
+// MWComponentName returns a MWOption that sets the component name
+// for the server-side span.
+func MWComponentName(componentName string) MWOption {
+	return func(options *mwOptions) {
+		options.componentName = componentName
+	}
+}
+
+// MWSpanFilter returns a MWOption that filters requests from creating a span
+// for the server-side span.
+// Span won't be created if it returns false.
+func MWSpanFilter(f func(r *http.Request) bool) MWOption {
+	return func(options *mwOptions) {
+		options.spanFilter = f
+	}
+}
+
+// MWSpanObserver returns a MWOption that observe the span
+// for the server-side span.
+func MWSpanObserver(f func(span opentracing.Span, r *http.Request)) MWOption {
+	return func(options *mwOptions) {
+		options.spanObserver = f
+	}
+}
+
+// MWURLTagFunc returns a MWOption that uses given function f
+// to set the span's http.url tag. Can be used to change the default
+// http.url tag, eg to redact sensitive information.
+func MWURLTagFunc(f func(u *url.URL) string) MWOption {
+	return func(options *mwOptions) {
+		options.urlTagFunc = f
+	}
+}
+
+// Middleware wraps an http.Handler and traces incoming requests.
+// Additionally, it adds the span to the request's context.
+//
+// By default, the operation name of the spans is set to "HTTP {method}".
+// This can be overriden with options.
+//
+// Example:
+// 	 http.ListenAndServe("localhost:80", nethttp.Middleware(tracer, http.DefaultServeMux))
+//
+// The options allow fine tuning the behavior of the middleware.
+//
+// Example:
+//   mw := nethttp.Middleware(
+//      tracer,
+//      http.DefaultServeMux,
+//      nethttp.OperationNameFunc(func(r *http.Request) string {
+//	        return "HTTP " + r.Method + ":/api/customers"
+//      }),
+//      nethttp.MWSpanObserver(func(sp opentracing.Span, r *http.Request) {
+//			sp.SetTag("http.uri", r.URL.EscapedPath())
+//		}),
+//   )
+func Middleware(tr opentracing.Tracer, h http.Handler, options ...MWOption) http.Handler {
+	return MiddlewareFunc(tr, h.ServeHTTP, options...)
+}
+
+// MiddlewareFunc wraps an http.HandlerFunc and traces incoming requests.
+// It behaves identically to the Middleware function above.
+//
+// Example:
+//   http.ListenAndServe("localhost:80", nethttp.MiddlewareFunc(tracer, MyHandler))
+func MiddlewareFunc(tr opentracing.Tracer, h http.HandlerFunc, options ...MWOption) http.HandlerFunc {
+	opts := mwOptions{
+		opNameFunc: func(r *http.Request) string {
+			return "HTTP " + r.Method
+		},
+		spanFilter:   func(r *http.Request) bool { return true },
+		spanObserver: func(span opentracing.Span, r *http.Request) {},
+		urlTagFunc: func(u *url.URL) string {
+			return u.String()
+		},
+	}
+	for _, opt := range options {
+		opt(&opts)
+	}
+	// set component name, use "net/http" if caller does not specify
+	componentName := opts.componentName
+	if componentName == "" {
+		componentName = defaultComponentName
+	}
+
+	fn := func(w http.ResponseWriter, r *http.Request) {
+		if !opts.spanFilter(r) {
+			h(w, r)
+			return
+		}
+		ctx, _ := tr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
+		sp := tr.StartSpan(opts.opNameFunc(r), ext.RPCServerOption(ctx))
+		ext.HTTPMethod.Set(sp, r.Method)
+		ext.HTTPUrl.Set(sp, opts.urlTagFunc(r.URL))
+		ext.Component.Set(sp, componentName)
+		opts.spanObserver(sp, r)
+
+		sct := &statusCodeTracker{ResponseWriter: w}
+		r = r.WithContext(opentracing.ContextWithSpan(r.Context(), sp))
+
+		defer func() {
+			panicErr := recover()
+			didPanic := panicErr != nil
+
+			if sct.status == 0 && !didPanic {
+				// Standard behavior of http.Server is to assume status code 200 if one was not written by a handler that returned successfully.
+				// https://github.com/golang/go/blob/fca286bed3ed0e12336532cc711875ae5b3cb02a/src/net/http/server.go#L120
+				sct.status = 200
+			}
+			if sct.status > 0 {
+				ext.HTTPStatusCode.Set(sp, uint16(sct.status))
+			}
+			if sct.status >= http.StatusInternalServerError || didPanic {
+				ext.Error.Set(sp, true)
+			}
+			sp.Finish()
+
+			if didPanic {
+				panic(panicErr)
+			}
+		}()
+
+		h(sct.wrappedResponseWriter(), r)
+	}
+	return http.HandlerFunc(fn)
+}

=== added file 'nethttp/server_test.go'
--- old/nethttp/server_test.go	1970-01-01 00:00:00 +0000
+++ new/nethttp/server_test.go	2020-06-08 03:54:42 +0000
@@ -0,0 +1,355 @@
+package nethttp
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"reflect"
+	"strings"
+	"testing"
+
+	"github.com/opentracing/opentracing-go"
+	"github.com/opentracing/opentracing-go/ext"
+	"github.com/opentracing/opentracing-go/mocktracer"
+)
+
+func TestOperationNameOption(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/root", func(w http.ResponseWriter, r *http.Request) {})
+
+	fn := func(r *http.Request) string {
+		return "HTTP " + r.Method + ": /root"
+	}
+
+	tests := []struct {
+		options []MWOption
+		opName  string
+	}{
+		{nil, "HTTP GET"},
+		{[]MWOption{OperationNameFunc(fn)}, "HTTP GET: /root"},
+	}
+
+	for _, tt := range tests {
+		testCase := tt
+		t.Run(testCase.opName, func(t *testing.T) {
+			tr := &mocktracer.MockTracer{}
+			mw := Middleware(tr, mux, testCase.options...)
+			srv := httptest.NewServer(mw)
+			defer srv.Close()
+
+			_, err := http.Get(srv.URL)
+			if err != nil {
+				t.Fatalf("server returned error: %v", err)
+			}
+
+			spans := tr.FinishedSpans()
+			if got, want := len(spans), 1; got != want {
+				t.Fatalf("got %d spans, expected %d", got, want)
+			}
+
+			if got, want := spans[0].OperationName, testCase.opName; got != want {
+				t.Fatalf("got %s operation name, expected %s", got, want)
+			}
+		})
+	}
+}
+
+func TestSpanObserverOption(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/root", func(w http.ResponseWriter, r *http.Request) {})
+
+	opNamefn := func(r *http.Request) string {
+		return "HTTP " + r.Method + ": /root"
+	}
+	spanObserverfn := func(sp opentracing.Span, r *http.Request) {
+		sp.SetTag("http.uri", r.URL.EscapedPath())
+	}
+	wantTags := map[string]interface{}{"http.uri": "/"}
+
+	tests := []struct {
+		options []MWOption
+		opName  string
+		Tags    map[string]interface{}
+	}{
+		{nil, "HTTP GET", nil},
+		{[]MWOption{OperationNameFunc(opNamefn)}, "HTTP GET: /root", nil},
+		{[]MWOption{MWSpanObserver(spanObserverfn)}, "HTTP GET", wantTags},
+		{[]MWOption{OperationNameFunc(opNamefn), MWSpanObserver(spanObserverfn)}, "HTTP GET: /root", wantTags},
+	}
+
+	for _, tt := range tests {
+		testCase := tt
+		t.Run(testCase.opName, func(t *testing.T) {
+			tr := &mocktracer.MockTracer{}
+			mw := Middleware(tr, mux, testCase.options...)
+			srv := httptest.NewServer(mw)
+			defer srv.Close()
+
+			_, err := http.Get(srv.URL)
+			if err != nil {
+				t.Fatalf("server returned error: %v", err)
+			}
+
+			spans := tr.FinishedSpans()
+			if got, want := len(spans), 1; got != want {
+				t.Fatalf("got %d spans, expected %d", got, want)
+			}
+
+			if got, want := spans[0].OperationName, testCase.opName; got != want {
+				t.Fatalf("got %s operation name, expected %s", got, want)
+			}
+
+			defaultLength := 5
+			if len(spans[0].Tags()) != len(testCase.Tags)+defaultLength {
+				t.Fatalf("got tag length %d, expected %d", len(spans[0].Tags()), len(testCase.Tags))
+			}
+			for k, v := range testCase.Tags {
+				if tag := spans[0].Tag(k); v != tag.(string) {
+					t.Fatalf("got %v tag, expected %v", tag, v)
+				}
+			}
+		})
+	}
+}
+
+func TestSpanFilterOption(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/root", func(w http.ResponseWriter, r *http.Request) {})
+
+	spanFilterfn := func(r *http.Request) bool {
+		return !strings.HasPrefix(r.Header.Get("User-Agent"), "kube-probe")
+	}
+	noAgentReq, _ := http.NewRequest("GET", "/root", nil)
+	noAgentReq.Header.Del("User-Agent")
+	probeReq1, _ := http.NewRequest("GET", "/root", nil)
+	probeReq1.Header.Add("User-Agent", "kube-probe/1.12")
+	probeReq2, _ := http.NewRequest("GET", "/root", nil)
+	probeReq2.Header.Add("User-Agent", "kube-probe/9.99")
+	postmanReq, _ := http.NewRequest("GET", "/root", nil)
+	postmanReq.Header.Add("User-Agent", "PostmanRuntime/7.3.0")
+	tests := []struct {
+		options            []MWOption
+		request            *http.Request
+		opName             string
+		ExpectToCreateSpan bool
+	}{
+		{nil, noAgentReq, "No filter", true},
+		{[]MWOption{MWSpanFilter(spanFilterfn)}, noAgentReq, "No User-Agent", true},
+		{[]MWOption{MWSpanFilter(spanFilterfn)}, probeReq1, "User-Agent: kube-probe/1.12", false},
+		{[]MWOption{MWSpanFilter(spanFilterfn)}, probeReq2, "User-Agent: kube-probe/9.99", false},
+		{[]MWOption{MWSpanFilter(spanFilterfn)}, postmanReq, "User-Agent: PostmanRuntime/7.3.0", true},
+	}
+
+	for _, tt := range tests {
+		testCase := tt
+		t.Run(testCase.opName, func(t *testing.T) {
+			tr := &mocktracer.MockTracer{}
+			mw := Middleware(tr, mux, testCase.options...)
+			srv := httptest.NewServer(mw)
+			defer srv.Close()
+
+			client := &http.Client{}
+			testCase.request.URL, _ = url.Parse(srv.URL)
+			_, err := client.Do(testCase.request)
+			if err != nil {
+				t.Fatalf("server returned error: %v", err)
+			}
+
+			spans := tr.FinishedSpans()
+			if spanCreated := len(spans) == 1; spanCreated != testCase.ExpectToCreateSpan {
+				t.Fatalf("spanCreated %t, ExpectToCreateSpan %t", spanCreated, testCase.ExpectToCreateSpan)
+			}
+		})
+	}
+}
+
+func TestURLTagOption(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/root", func(w http.ResponseWriter, r *http.Request) {})
+
+	fn := func(u *url.URL) string {
+		// Log path only (no query parameters etc)
+		return u.Path
+	}
+
+	tests := []struct {
+		options []MWOption
+		url     string
+		tag     string
+	}{
+		{[]MWOption{}, "/root?token=123", "/root?token=123"},
+		{[]MWOption{MWURLTagFunc(fn)}, "/root?token=123", "/root"},
+	}
+
+	for _, tt := range tests {
+		testCase := tt
+		t.Run(testCase.tag, func(t *testing.T) {
+			tr := &mocktracer.MockTracer{}
+			mw := Middleware(tr, mux, testCase.options...)
+			srv := httptest.NewServer(mw)
+			defer srv.Close()
+
+			_, err := http.Get(srv.URL + testCase.url)
+			if err != nil {
+				t.Fatalf("server returned error: %v", err)
+			}
+
+			spans := tr.FinishedSpans()
+			if got, want := len(spans), 1; got != want {
+				t.Fatalf("got %d spans, expected %d", got, want)
+			}
+
+			tag := spans[0].Tags()["http.url"]
+			if got, want := tag, testCase.tag; got != want {
+				t.Fatalf("got %s tag name, expected %s", got, want)
+			}
+		})
+	}
+}
+
+func TestSpanErrorAndStatusCode(t *testing.T) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/header-and-body", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(200)
+		w.Write([]byte("OK"))
+	})
+	mux.HandleFunc("/body-only", func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("OK"))
+	})
+	mux.HandleFunc("/header-only", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(200)
+	})
+	mux.HandleFunc("/empty", func(w http.ResponseWriter, r *http.Request) {
+		// no status header
+	})
+	mux.HandleFunc("/error", func(w http.ResponseWriter, r *http.Request) {
+		w.WriteHeader(500)
+	})
+
+	expStatusOK := map[string]interface{}{"http.status_code": uint16(200)}
+
+	tests := []struct {
+		url  string
+		tags map[string]interface{}
+	}{
+		{url: "/header-and-body", tags: expStatusOK},
+		{url: "/body-only", tags: expStatusOK},
+		{url: "/header-only", tags: expStatusOK},
+		{url: "/empty", tags: expStatusOK},
+		{url: "/error", tags: map[string]interface{}{"http.status_code": uint16(500), string(ext.Error): true}},
+	}
+
+	for _, tt := range tests {
+		testCase := tt
+		t.Run(testCase.url, func(t *testing.T) {
+			tr := &mocktracer.MockTracer{}
+			mw := Middleware(tr, mux)
+			srv := httptest.NewServer(mw)
+			defer srv.Close()
+
+			_, err := http.Get(srv.URL + testCase.url)
+			if err != nil {
+				t.Fatalf("server returned error: %v", err)
+			}
+
+			spans := tr.FinishedSpans()
+			if got, want := len(spans), 1; got != want {
+				t.Fatalf("got %d spans, expected %d", got, want)
+			}
+
+			for k, v := range testCase.tags {
+				if tag := spans[0].Tag(k); !reflect.DeepEqual(tag, v) {
+					t.Fatalf("tag %s: got %v, expected %v", k, tag, v)
+				}
+			}
+		})
+	}
+}
+
+func BenchmarkStatusCodeTrackingOverhead(b *testing.B) {
+	mux := http.NewServeMux()
+	mux.HandleFunc("/root", func(w http.ResponseWriter, r *http.Request) {})
+	tr := &mocktracer.MockTracer{}
+	mw := Middleware(tr, mux)
+	srv := httptest.NewServer(mw)
+	defer srv.Close()
+
+	b.RunParallel(func(pb *testing.PB) {
+		for pb.Next() {
+			resp, err := http.Get(srv.URL)
+			if err != nil {
+				b.Fatalf("server returned error: %v", err)
+			}
+			err = resp.Body.Close()
+			if err != nil {
+				b.Fatalf("failed to close response: %v", err)
+			}
+		}
+	})
+}
+
+func TestMiddlewareHandlerPanic(t *testing.T) {
+	tests := []struct {
+		handler func(w http.ResponseWriter, r *http.Request)
+		status  uint16
+		isError bool
+		name    string
+	}{
+		{
+			name: "OK",
+			handler: func(w http.ResponseWriter, r *http.Request) {
+				w.Write([]byte("OK"))
+			},
+			status:  200,
+			isError: false,
+		},
+		{
+			name: "Panic",
+			handler: func(w http.ResponseWriter, r *http.Request) {
+				panic("panic test")
+			},
+			status:  0,
+			isError: true,
+		},
+		{
+			name: "InternalServerError",
+			handler: func(w http.ResponseWriter, r *http.Request) {
+				w.WriteHeader(http.StatusInternalServerError)
+				w.Write([]byte("InternalServerError"))
+			},
+			status:  500,
+			isError: true,
+		},
+	}
+
+	for _, testCase := range tests {
+		t.Run(testCase.name, func(t *testing.T) {
+			mux := http.NewServeMux()
+			mux.HandleFunc("/root", testCase.handler)
+			tr := &mocktracer.MockTracer{}
+			srv := httptest.NewServer(MiddlewareFunc(tr, mux.ServeHTTP))
+			defer srv.Close()
+
+			_, err := http.Get(srv.URL + "/root")
+			if err != nil {
+				t.Logf("server returned error: %v", err)
+			}
+
+			spans := tr.FinishedSpans()
+			if got, want := len(spans), 1; got != want {
+				t.Fatalf("got %d spans, expected %d", got, want)
+			}
+			actualStatus := spans[0].Tag(string(ext.HTTPStatusCode))
+			if testCase.status > 0 && !reflect.DeepEqual(testCase.status, actualStatus) {
+				t.Fatalf("got status code %v, expected %d", actualStatus, testCase.status)
+			}
+			actualErr, ok := spans[0].Tag(string(ext.Error)).(bool)
+			if !ok {
+				actualErr = false
+			}
+			if testCase.isError != actualErr {
+				t.Fatalf("got span error %v, expected %v", actualErr, testCase.isError)
+			}
+		})
+	}
+}

=== added file 'nethttp/status-code-tracker.go'
--- old/nethttp/status-code-tracker.go	1970-01-01 00:00:00 +0000
+++ new/nethttp/status-code-tracker.go	2020-06-08 03:54:42 +0000
@@ -0,0 +1,251 @@
+// +build go1.8
+
+package nethttp
+
+import (
+	"io"
+	"net/http"
+)
+
+type statusCodeTracker struct {
+	http.ResponseWriter
+	status int
+}
+
+func (w *statusCodeTracker) WriteHeader(status int) {
+	w.status = status
+	w.ResponseWriter.WriteHeader(status)
+}
+
+func (w *statusCodeTracker) Write(b []byte) (int, error) {
+	return w.ResponseWriter.Write(b)
+}
+
+// wrappedResponseWriter returns a wrapped version of the original
+// ResponseWriter and only implements the same combination of additional
+// interfaces as the original.  This implementation is based on
+// https://github.com/felixge/httpsnoop.
+func (w *statusCodeTracker) wrappedResponseWriter() http.ResponseWriter {
+	var (
+		hj, i0 = w.ResponseWriter.(http.Hijacker)
+		cn, i1 = w.ResponseWriter.(http.CloseNotifier)
+		pu, i2 = w.ResponseWriter.(http.Pusher)
+		fl, i3 = w.ResponseWriter.(http.Flusher)
+		rf, i4 = w.ResponseWriter.(io.ReaderFrom)
+	)
+
+	switch {
+	case !i0 && !i1 && !i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+		}{w}
+	case !i0 && !i1 && !i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			io.ReaderFrom
+		}{w, rf}
+	case !i0 && !i1 && !i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Flusher
+		}{w, fl}
+	case !i0 && !i1 && !i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Flusher
+			io.ReaderFrom
+		}{w, fl, rf}
+	case !i0 && !i1 && i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Pusher
+		}{w, pu}
+	case !i0 && !i1 && i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Pusher
+			io.ReaderFrom
+		}{w, pu, rf}
+	case !i0 && !i1 && i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Pusher
+			http.Flusher
+		}{w, pu, fl}
+	case !i0 && !i1 && i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Pusher
+			http.Flusher
+			io.ReaderFrom
+		}{w, pu, fl, rf}
+	case !i0 && i1 && !i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+		}{w, cn}
+	case !i0 && i1 && !i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+			io.ReaderFrom
+		}{w, cn, rf}
+	case !i0 && i1 && !i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+			http.Flusher
+		}{w, cn, fl}
+	case !i0 && i1 && !i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+			http.Flusher
+			io.ReaderFrom
+		}{w, cn, fl, rf}
+	case !i0 && i1 && i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+			http.Pusher
+		}{w, cn, pu}
+	case !i0 && i1 && i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+			http.Pusher
+			io.ReaderFrom
+		}{w, cn, pu, rf}
+	case !i0 && i1 && i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+			http.Pusher
+			http.Flusher
+		}{w, cn, pu, fl}
+	case !i0 && i1 && i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.CloseNotifier
+			http.Pusher
+			http.Flusher
+			io.ReaderFrom
+		}{w, cn, pu, fl, rf}
+	case i0 && !i1 && !i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+		}{w, hj}
+	case i0 && !i1 && !i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			io.ReaderFrom
+		}{w, hj, rf}
+	case i0 && !i1 && !i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.Flusher
+		}{w, hj, fl}
+	case i0 && !i1 && !i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.Flusher
+			io.ReaderFrom
+		}{w, hj, fl, rf}
+	case i0 && !i1 && i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.Pusher
+		}{w, hj, pu}
+	case i0 && !i1 && i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.Pusher
+			io.ReaderFrom
+		}{w, hj, pu, rf}
+	case i0 && !i1 && i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.Pusher
+			http.Flusher
+		}{w, hj, pu, fl}
+	case i0 && !i1 && i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.Pusher
+			http.Flusher
+			io.ReaderFrom
+		}{w, hj, pu, fl, rf}
+	case i0 && i1 && !i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+		}{w, hj, cn}
+	case i0 && i1 && !i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+			io.ReaderFrom
+		}{w, hj, cn, rf}
+	case i0 && i1 && !i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+			http.Flusher
+		}{w, hj, cn, fl}
+	case i0 && i1 && !i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+			http.Flusher
+			io.ReaderFrom
+		}{w, hj, cn, fl, rf}
+	case i0 && i1 && i2 && !i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+			http.Pusher
+		}{w, hj, cn, pu}
+	case i0 && i1 && i2 && !i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+			http.Pusher
+			io.ReaderFrom
+		}{w, hj, cn, pu, rf}
+	case i0 && i1 && i2 && i3 && !i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+			http.Pusher
+			http.Flusher
+		}{w, hj, cn, pu, fl}
+	case i0 && i1 && i2 && i3 && i4:
+		return struct {
+			http.ResponseWriter
+			http.Hijacker
+			http.CloseNotifier
+			http.Pusher
+			http.Flusher
+			io.ReaderFrom
+		}{w, hj, cn, pu, fl, rf}
+	default:
+		return struct {
+			http.ResponseWriter
+		}{w}
+	}
+}