Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package fortio for openSUSE:Factory checked 
in at 2022-09-21 14:43:12
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/fortio (Old)
 and      /work/SRC/openSUSE:Factory/.fortio.new.2083 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "fortio"

Wed Sep 21 14:43:12 2022 rev:21 rq:1005152 version:1.38.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/fortio/fortio.changes    2022-09-14 
13:45:10.157922526 +0200
+++ /work/SRC/openSUSE:Factory/.fortio.new.2083/fortio.changes  2022-09-21 
14:44:11.422036955 +0200
@@ -1,0 +2,7 @@
+Wed Sep 21 08:29:20 UTC 2022 - ka...@b1-systems.de
+
+- Update to version 1.38.0:
+  * only do compression if requested explictly when forwarding/fetching  (#625)
+  * add connection times histogram (#626)
+
+-------------------------------------------------------------------

Old:
----
  fortio-1.37.1.tar.gz

New:
----
  fortio-1.38.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ fortio.spec ++++++
--- /var/tmp/diff_new_pack.BgHovk/_old  2022-09-21 14:44:11.942038312 +0200
+++ /var/tmp/diff_new_pack.BgHovk/_new  2022-09-21 14:44:11.946038323 +0200
@@ -19,7 +19,7 @@
 %define __arch_install_post export NO_BRP_STRIP_DEBUG=true
 
 Name:           fortio
-Version:        1.37.1
+Version:        1.38.0
 Release:        0
 Summary:        Load testing library, command line tool, advanced echo server 
and web UI
 License:        Apache-2.0

++++++ _service ++++++
--- /var/tmp/diff_new_pack.BgHovk/_old  2022-09-21 14:44:11.982038417 +0200
+++ /var/tmp/diff_new_pack.BgHovk/_new  2022-09-21 14:44:11.986038427 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/fortio/fortio</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v1.37.1</param>
+    <param name="revision">v1.38.0</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="changesgenerate">enable</param>
     <param name="versionrewrite-pattern">v(.*)</param>
@@ -17,7 +17,7 @@
     <param name="compression">gz</param>
   </service>
   <service name="go_modules" mode="disabled">
-    <param name="archive">fortio-1.37.1.tar.gz</param>
+    <param name="archive">fortio-1.38.0.tar.gz</param>
   </service>
 </services>
 

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.BgHovk/_old  2022-09-21 14:44:12.006038480 +0200
+++ /var/tmp/diff_new_pack.BgHovk/_new  2022-09-21 14:44:12.010038490 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/fortio/fortio</param>
-              <param 
name="changesrevision">0fa89821fab2d9451802ea6195b721d4a94d65a7</param></service></servicedata>
+              <param 
name="changesrevision">c9eaf5e4579a36ca1e341a64873626fa49fb97b2</param></service></servicedata>
 (No newline at EOF)
 

++++++ fortio-1.37.1.tar.gz -> fortio-1.38.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/README.md new/fortio-1.38.0/README.md
--- old/fortio-1.37.1/README.md 2022-09-13 19:31:18.000000000 +0200
+++ new/fortio-1.38.0/README.md 2022-09-19 23:06:27.000000000 +0200
@@ -52,13 +52,13 @@
 The [releases](https://github.com/fortio/fortio/releases) page has binaries 
for many OS/architecture combinations (see assets).
 
 ```shell
-curl -L 
https://github.com/fortio/fortio/releases/download/v1.37.1/fortio-linux_amd64-1.37.1.tgz
 \
+curl -L 
https://github.com/fortio/fortio/releases/download/v1.38.0/fortio-linux_amd64-1.38.0.tgz
 \
  | sudo tar -C / -xvzpf -
 # or the debian package
-wget 
https://github.com/fortio/fortio/releases/download/v1.37.1/fortio_1.37.1_amd64.deb
-dpkg -i fortio_1.37.1_amd64.deb
+wget 
https://github.com/fortio/fortio/releases/download/v1.38.0/fortio_1.38.0_amd64.deb
+dpkg -i fortio_1.38.0_amd64.deb
 # or the rpm
-rpm -i 
https://github.com/fortio/fortio/releases/download/v1.37.1/fortio-1.37.1-1.x86_64.rpm
+rpm -i 
https://github.com/fortio/fortio/releases/download/v1.38.0/fortio-1.38.0-1.x86_64.rpm
 # and more, see assets in release page
 ```
 
@@ -68,7 +68,7 @@
 brew install fortio
 ```
 
-On Windows, download 
https://github.com/fortio/fortio/releases/download/v1.37.1/fortio_win_1.37.1.zip
 and extract `fortio.exe` to any location, then using the Windows Command 
Prompt:
+On Windows, download 
https://github.com/fortio/fortio/releases/download/v1.38.0/fortio_win_1.38.0.zip
 and extract `fortio.exe` to any location, then using the Windows Command 
Prompt:
 ```
 fortio.exe server
 ```
@@ -116,7 +116,7 @@
 <details>
 <!-- use release/updateFlags.sh to update this section -->
 <pre>
-???????????? 1.37.1 usage:
+???????????? 1.38.0 usage:
     fortio command [flags] target
 where command is one of: load (load testing), server (starts ui, rest api,
  http-echo, redirect, proxies, tcp-echo and grpc ping servers), tcp-echo (only
@@ -360,6 +360,8 @@
 | header    | header(s) to add to the reply e.g. `&header=Foo:Bar&header=X:Y` |
 | gzip      | If `Accept-Encoding: gzip` is passed in headers by the 
caller/client; and `gzip=true` is in the query args, all response will be 
gzipped; or if `gzip=42.7` is passed, approximately 42.7% will|
 
+`delay`, `close` and `header` query arguments are also supported for the 
`debug` endpoint which echoes back the request (gzip is always done if 
`Accept-Encoding: gzip` is present, status is always 200, and the payload is 
the echo back debug information).
+
 You can set a default value for all these by passing 
`-echo-server-default-params` to the server command line, for instance:
 `fortio server -echo-server-default-params="delay=0.5s:50,1s:40&status=418"` 
will make the server respond with http 418 and a delay of either 0.5s half of 
the time, 1s 40% and no delay in 10% of the calls; unless any `?` query args is 
passed by the client. Note that the quotes (&quot;) are for the shell to escape 
the ampersand (&amp;) but should not be put in a yaml nor the dynamicflag url 
for instance.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/http_client.go 
new/fortio-1.38.0/fhttp/http_client.go
--- old/fortio-1.37.1/fhttp/http_client.go      2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/fhttp/http_client.go      2022-09-19 23:06:27.000000000 
+0200
@@ -45,9 +45,9 @@
        Fetch() (int, []byte, int)
        // Close() cleans up connections and state - must be paired with 
NewClient calls.
        Close()
-       // GetIPAddress() returns the occurrence of ip address used by this 
client connection.
-       // and how many sockets have been used
-       GetIPAddress() (*stats.Occurrence, int)
+       // GetIPAddress() returns the occurrence of ip address used by this 
client connection,
+       // and the connection time histogram (which includes the count).
+       GetIPAddress() (*stats.Occurrence, *stats.Histogram)
 }
 
 const (
@@ -90,6 +90,9 @@
                log.Warnf("Invalid timeout %v, setting to %v", 
h.HTTPReqTimeOut, HTTPReqTimeOutDefaultValue)
                h.HTTPReqTimeOut = HTTPReqTimeOutDefaultValue
        }
+       if h.Resolution <= 0 {
+               h.Resolution = 0.001
+       }
        h.URLSchemeCheck()
        return h
 }
@@ -185,6 +188,10 @@
        ConnReuseRange   [2]int        // range of max number of connection to 
reuse for each thread.
        // When false, re-resolve the DNS name when the connection breaks.
        NoResolveEachConn bool
+       // Optional Offset Duration; to offset the histogram of the Connection 
duration
+       Offset time.Duration
+       // Optional resolution divider for the Connection duration histogram. 
In seconds. Defaults to 0.001 or 1 millisecond.
+       Resolution float64
 }
 
 // ResetHeaders resets all the headers, including the User-Agent: one (and the 
Host: logical special header).
@@ -354,8 +361,8 @@
        bodyContainsUUID     bool // if body contains the "{uuid}" pattern 
(lowercase)
        logErrors            bool
        id                   int
-       socketCount          int
        ipAddrUsage          *stats.Occurrence
+       connectStats         *stats.Histogram
 }
 
 // Close cleans up any resources used by NewStdClient.
@@ -443,9 +450,9 @@
        return code, data, 0
 }
 
-// GetIPAddress get the ip address that DNS resolves to when using stdClient.
-func (c *Client) GetIPAddress() (*stats.Occurrence, int) {
-       return c.ipAddrUsage, c.socketCount
+// GetIPAddress get the ip address that DNS resolves to when using stdClient 
and connection stats.
+func (c *Client) GetIPAddress() (*stats.Occurrence, *stats.Histogram) {
+       return c.ipAddrUsage, c.connectStats
 }
 
 // NewClient creates either a standard or fast client (depending on
@@ -483,6 +490,8 @@
                id:          o.ID,
                logErrors:   o.LogErrors,
                ipAddrUsage: stats.NewOccurrence(),
+               // Keep track of timing for connection (re)establishment.
+               connectStats: stats.NewHistogram(o.Offset.Seconds(), 
o.Resolution),
        }
 
        tr := http.Transport{
@@ -497,10 +506,11 @@
                                addr = o.Resolve + addr[strings.LastIndex(addr, 
":"):]
                        }
                        var conn net.Conn
+                       now := time.Now()
                        conn, err = (&net.Dialer{
                                Timeout: o.HTTPReqTimeOut,
                        }).DialContext(ctx, network, addr)
-
+                       client.connectStats.Record(time.Since(now).Seconds())
                        if conn != nil {
                                newRemoteAddress := conn.RemoteAddr().String()
                                // No change when it wasn't set before (first 
time) and when the value isn't actually changing either.
@@ -508,10 +518,8 @@
                                        log.Infof("[%d] Standard client IP 
address changed from %s to %s", client.id, req.RemoteAddr, newRemoteAddress)
                                }
                                req.RemoteAddr = newRemoteAddress
-                               client.socketCount++
                                client.ipAddrUsage.Record(req.RemoteAddr)
                        }
-
                        return conn, err
                },
                TLSHandshakeTimeout: o.HTTPReqTimeOut,
@@ -561,7 +569,7 @@
        req          []byte
        dest         net.Addr
        socket       net.Conn
-       socketCount  int
+       socketCount  int // number of sockets attempts, same as the new 
connectStats.Count() + DNS errors if any.
        size         int
        code         int
        errorCount   int
@@ -588,11 +596,12 @@
        connReuseRange [2]int
        connReuse      int
        reuseCount     int
+       connectStats   *stats.Histogram
 }
 
-// GetIPAddress get ip address that DNS resolved to when using fast client.
-func (c *FastClient) GetIPAddress() (*stats.Occurrence, int) {
-       return c.ipAddrUsage, c.socketCount
+// GetIPAddress get ip address that DNS resolved to when using fast client and 
connection stats.
+func (c *FastClient) GetIPAddress() (*stats.Occurrence, *stats.Histogram) {
+       return c.ipAddrUsage, c.connectStats
 }
 
 // Close cleans up any resources used by FastClient.
@@ -657,6 +666,8 @@
                http10: o.HTTP10, halfClose: o.AllowHalfClose, logErrors: 
o.LogErrors, id: o.ID,
                https: o.https, connReuseRange: o.ConnReuseRange, connReuse: 
connReuse,
                resolve: o.Resolve, noResolveEachConn: o.NoResolveEachConn, 
ipAddrUsage: stats.NewOccurrence(),
+               // Keep track of timing for connection (re)establishment.
+               connectStats: stats.NewHistogram(o.Offset.Seconds(), 
o.Resolution),
        }
        if o.https {
                bc.tlsConfig, err = o.TLSOptions.TLSClientConfig()
@@ -751,14 +762,17 @@
        }
 
        d := &net.Dialer{Timeout: c.reqTimeout}
+       now := time.Now()
        if c.https {
                socket, err = tls.DialWithDialer(d, c.dest.Network(), 
c.dest.String(), c.tlsConfig)
+               c.connectStats.Record(time.Since(now).Seconds())
                if err != nil {
                        log.Errf("[%d] Unable to TLS connect to %v : %v", c.id, 
c.dest, err)
                        return nil
                }
        } else {
                socket, err = d.Dial(c.dest.Network(), c.dest.String())
+               c.connectStats.Record(time.Since(now).Seconds())
                if err != nil {
                        log.Errf("[%d] Unable to connect to %v : %v", c.id, 
c.dest, err)
                        return nil
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/http_forwarder.go 
new/fortio-1.38.0/fhttp/http_forwarder.go
--- old/fortio-1.37.1/fhttp/http_forwarder.go   2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/fhttp/http_forwarder.go   2022-09-19 23:06:27.000000000 
+0200
@@ -247,6 +247,9 @@
                        // TODO make configurable, should be fine for now for 
most but extreme -c values
                        MaxIdleConnsPerHost: 128, // must be more than incoming 
parallelization; divided by number of fan out if using parallel mode
                        MaxIdleConns:        256,
+                       // This avoids Accept-Encoding: gzip being added to 
outgoing requests when no encoding accept is specified
+                       // yet if passed by request, it will do gzip end to 
end. Issue #624.
+                       DisableCompression: true,
                },
        }
        return client
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/http_forwarder_test.go 
new/fortio-1.38.0/fhttp/http_forwarder_test.go
--- old/fortio-1.37.1/fhttp/http_forwarder_test.go      2022-09-13 
19:31:18.000000000 +0200
+++ new/fortio-1.38.0/fhttp/http_forwarder_test.go      2022-09-19 
23:06:27.000000000 +0200
@@ -43,6 +43,10 @@
                if !bytes.Contains(data, []byte(payload)) {
                        t.Errorf("Result %s doesn't contain expected payload 
echo back %q", DebugSummary(data, 1024), payload)
                }
+               // Issue #624
+               if bytes.Contains(data, []byte("gzip")) {
+                       t.Errorf("Result %s contains unexpected gzip (accept 
encoding)", DebugSummary(data, 1024))
+               }
                if !bytes.Contains(data, []byte("X-Fortio-Multi-Id: 1")) {
                        t.Errorf("Result %s doesn't contain expected 
X-Fortio-Multi-Id: 1", DebugSummary(data, 1024))
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/http_loglevel_test.go 
new/fortio-1.38.0/fhttp/http_loglevel_test.go
--- old/fortio-1.37.1/fhttp/http_loglevel_test.go       2022-09-13 
19:31:18.000000000 +0200
+++ new/fortio-1.38.0/fhttp/http_loglevel_test.go       2022-09-19 
23:06:27.000000000 +0200
@@ -32,3 +32,8 @@
        TestNoFirstChunkSizeInitially(t)
        TestFetchAndOnBehalfOf(t)
 }
+
+func TesWarningMode(t *testing.T) {
+       log.SetLogLevel(log.Warning)
+       TestHTTPRunner(t)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/http_server.go 
new/fortio-1.38.0/fhttp/http_server.go
--- old/fortio-1.37.1/fhttp/http_server.go      2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/fhttp/http_server.go      2022-09-19 23:06:27.000000000 
+0200
@@ -33,6 +33,7 @@
 
        "fortio.org/fortio/dflag"
        "fortio.org/fortio/fnet"
+       "fortio.org/fortio/jrpc"
        "fortio.org/fortio/log"
        "fortio.org/fortio/version"
        "golang.org/x/net/http2"
@@ -81,11 +82,7 @@
                return
        }
        log.Debugf("Read %d", len(data))
-       dur := generateDelay(r.FormValue("delay"))
-       if dur > 0 {
-               log.LogVf("Sleeping for %v", dur)
-               time.Sleep(dur)
-       }
+       handleCommonArgs(w, r)
        statusStr := r.FormValue("status")
        var status int
        if statusStr != "" {
@@ -93,34 +90,12 @@
        } else {
                status = http.StatusOK
        }
-       if log.LogDebug() {
-               // TODO: this easily lead to contention - use 'thread local'
-               rqNum := atomic.AddInt64(&EchoRequests, 1)
-               log.Debugf("Request # %v", rqNum)
-       }
-       if generateClose(r.FormValue("close")) {
-               log.Debugf("Adding Connection:close / will close socket")
-               w.Header().Set("Connection", "close")
-       }
        gzip := strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") && 
generateGzip(r.FormValue("gzip"))
        if gzip {
                gwz := NewGzipHTTPResponseWriter(w)
                defer gwz.Close()
                w = gwz
        }
-       // process header(s) args, must be before size to compose properly
-       for _, hdr := range r.Form["header"] {
-               log.LogVf("Adding requested header %s", hdr)
-               if len(hdr) == 0 {
-                       continue
-               }
-               s := strings.SplitN(hdr, ":", 2)
-               if len(s) != 2 {
-                       log.Errf("invalid extra header '%s', expecting Key: 
Value", hdr)
-                       continue
-               }
-               w.Header().Add(s[0], s[1])
-       }
        size := generateSize(r.FormValue("size"))
        if size >= 0 {
                log.LogVf("Writing %d size with %d status", size, status)
@@ -130,7 +105,7 @@
        // echo back the Content-Type and Content-Length in the response
        for _, k := range []string{"Content-Type", "Content-Length"} {
                if v := r.Header.Get(k); v != "" {
-                       w.Header().Set(k, v)
+                       jrpc.SetHeaderIfMissing(w.Header(), k, v)
                }
        }
        w.WriteHeader(status)
@@ -139,8 +114,40 @@
        }
 }
 
+// handleCommonArgs common flags for debug and echo handlers.
+// Must be called after body is read.
+func handleCommonArgs(w http.ResponseWriter, r *http.Request) {
+       dur := generateDelay(r.FormValue("delay"))
+       if dur > 0 {
+               log.LogVf("Sleeping for %v", dur)
+               time.Sleep(dur)
+       }
+       if log.LogDebug() {
+               // Note this easily lead to contention, debug mode only (or low 
qps).
+               rqNum := atomic.AddInt64(&EchoRequests, 1)
+               log.Debugf("Request # %v", rqNum)
+       }
+       if generateClose(r.FormValue("close")) {
+               log.Debugf("Adding Connection:close / will close socket")
+               w.Header().Set("Connection", "close")
+       }
+       // process header(s) args, must be before size to compose properly
+       for _, hdr := range r.Form["header"] {
+               log.LogVf("Adding requested header %s", hdr)
+               if len(hdr) == 0 {
+                       continue
+               }
+               s := strings.SplitN(hdr, ":", 2)
+               if len(s) != 2 {
+                       log.Errf("invalid extra header '%s', expecting Key: 
Value", hdr)
+                       continue
+               }
+               w.Header().Add(s[0], s[1])
+       }
+}
+
 func writePayload(w http.ResponseWriter, status int, size int) {
-       w.Header().Set("Content-Type", "application/octet-stream")
+       jrpc.SetHeaderIfMissing(w.Header(), "Content-Type", 
"application/octet-stream")
        w.Header().Set("Content-Length", strconv.Itoa(size))
        w.WriteHeader(status)
        n, err := w.Write(fnet.Payload[:size])
@@ -291,7 +298,7 @@
        buf.WriteString("Host: ")
        buf.WriteString(r.Host)
 
-       var keys []string //nolint:prealloc // header is multi valued map,...
+       keys := make([]string, 0, len(r.Header))
        for k := range r.Header {
                keys = append(keys, k)
        }
@@ -337,7 +344,8 @@
                        buf.WriteByte('\n')
                }
        }
-       w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
+       handleCommonArgs(w, r)
+       jrpc.SetHeaderIfMissing(w.Header(), "Content-Type", "text/plain; 
charset=UTF-8")
        if _, err = w.Write(buf.Bytes()); err != nil {
                log.Errf("Error writing response %v to %v", err, r.RemoteAddr)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/http_test.go 
new/fortio-1.38.0/fhttp/http_test.go
--- old/fortio-1.37.1/fhttp/http_test.go        2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/fhttp/http_test.go        2022-09-19 23:06:27.000000000 
+0200
@@ -1107,34 +1107,46 @@
        }
 }
 
+// TestDebugHandlerSortedHeaders tests the headers are sorted but
+// also tests post echo back and gzip handling.
 func TestDebugHandlerSortedHeaders(t *testing.T) {
        m, a := DynamicHTTPServer(false)
-       m.HandleFunc("/debug", DebugHandler)
-       url := fmt.Sprintf("http://localhost:%d/debug";, a.Port)
-       o := HTTPOptions{URL: url, DisableFastClient: true}
+       m.Handle("/debug", Gzip(http.HandlerFunc(DebugHandler))) // same as in 
Serve()
+       // Debug handler does respect the delay arg but not status, status is 
always 200
+       url := fmt.Sprintf("http://localhost:%d/debug?delay=500ms&status=555";, 
a.Port)
+       // Trigger transparent compression (which will add Accept-Encoding: 
gzip header)
+       o := HTTPOptions{URL: url, DisableFastClient: true, Compression: true, 
Payload: []byte("abcd")}
        o.AddAndValidateExtraHeader("BBB: bbb")
        o.AddAndValidateExtraHeader("CCC: ccc")
        o.AddAndValidateExtraHeader("ZZZ: zzz")
        o.AddAndValidateExtraHeader("AAA: aaa")
        client, _ := NewClient(&o)
+       now := time.Now()
        code, data, header := client.Fetch() // used to panic/bug #127
+       duration := time.Since(now)
        t.Logf("TestDebugHandlerSortedHeaders result code %d, data len %d, 
headerlen %d", code, len(data), header)
        if code != http.StatusOK {
                t.Errorf("Got %d instead of 200", code)
        }
+       if duration < 500*time.Millisecond {
+               t.Errorf("Got %s instead of 500ms", duration)
+       }
        // remove the first line ('???????????? version...') from the body
        body := string(data)
        i := strings.Index(body, "\n")
        body = body[i+1:]
-       expected := fmt.Sprintf("\nGET /debug HTTP/1.1\n\n"+
+       expected := fmt.Sprintf("\nPOST /debug?delay=500ms&status=555 
HTTP/1.1\n\n"+
                "headers:\n\n"+
                "Host: localhost:%d\n"+
                "Aaa: aaa\n"+
+               "Accept-Encoding: gzip\n"+
                "Bbb: bbb\n"+
                "Ccc: ccc\n"+
+               "Content-Length: 4\n"+
+               "Content-Type: application/octet-stream\n"+
                "User-Agent: %s\n"+
                "Zzz: zzz\n\n"+
-               "body:\n\n\n", a.Port, jrpc.UserAgent)
+               "body:\n\nabcd\n", a.Port, jrpc.UserAgent)
        if body != expected {
                t.Errorf("Get body: %s not as expected: %s", body, expected)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/httprunner.go 
new/fortio-1.38.0/fhttp/httprunner.go
--- old/fortio-1.37.1/fhttp/httprunner.go       2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/fhttp/httprunner.go       2022-09-19 23:06:27.000000000 
+0200
@@ -45,8 +45,10 @@
        HTTPOptions
        Sizes       *stats.HistogramData
        HeaderSizes *stats.HistogramData
-       Sockets     []int
-       SocketCount int
+       Sockets     []int64
+       SocketCount int64
+       // Connection Time stats
+       ConnectionStats *stats.HistogramData
        // http code to abort the run on (-1 for connection or other socket 
error)
        AbortOn int
        aborter *periodic.Aborter
@@ -85,7 +87,7 @@
 
 // RunHTTPTest runs an http test and returns the aggregated stats.
 //
-//nolint:funlen, gocognit, gocyclo
+//nolint:funlen, gocognit, gocyclo, maintidx
 func RunHTTPTest(o *HTTPRunnerOptions) (*HTTPRunnerResults, error) {
        o.RunType = "HTTP"
        warmupMode := "parallel"
@@ -100,6 +102,12 @@
        log.Infof("Starting http test for %s with %d threads at %.1f qps and %s 
warmup%s",
                o.URL, o.NumThreads, o.QPS, warmupMode, connReuseMsg)
        r := periodic.NewPeriodicRunner(&o.RunnerOptions)
+       if o.HTTPOptions.Resolution <= 0 {
+               // Set both connect histogram params when Resolution isn't set 
explicitly on the HTTP options
+               // (that way you can set the offet to 0 in connect and to 
something else for the call)
+               o.HTTPOptions.Resolution = r.Options().Resolution
+               o.HTTPOptions.Offset = r.Options().Offset
+       }
        defer r.Options().Abort()
        numThreads := r.Options().NumThreads // can change during run for c > 2 
n
        o.HTTPOptions.Init(o.URL)
@@ -188,6 +196,8 @@
                fm.Close()
                _, _ = fmt.Fprintf(out, "Wrote profile data to %s.{cpu|mem}\n", 
o.Profiler)
        }
+       // Connection stats, aggregated
+       connectionStats := stats.NewHistogram(o.HTTPOptions.Offset.Seconds(), 
o.HTTPOptions.Resolution)
        // Numthreads may have reduced:
        numThreads = total.RunnerResults.NumThreads
        // But we also must cleanup all the created clients.
@@ -195,10 +205,11 @@
        fmt.Fprintf(out, "# Socket and IP used for each connection:\n")
        for i := 0; i < numThreads; i++ {
                // Get the report on the IP address each thread use to send 
traffic
-               occurrence, currentSocketUsed := 
httpstate[i].client.GetIPAddress()
+               occurrence, connStats := httpstate[i].client.GetIPAddress()
+               currentSocketUsed := connStats.Count
                httpstate[i].client.Close()
-               fmt.Fprintf(out, "[%d] %3d socket used, resolved to %s\n", i, 
currentSocketUsed, occurrence.PrintAndAggregate(total.IPCountMap))
-
+               fmt.Fprintf(out, "[%d] %3d socket used, resolved to %s ", i, 
currentSocketUsed, occurrence.PrintAndAggregate(total.IPCountMap))
+               connStats.Counter.Print(out, "connection timing")
                total.SocketCount += currentSocketUsed
                total.Sockets = append(total.Sockets, currentSocketUsed)
                // Q: is there some copying each time stats[i] is used?
@@ -210,6 +221,13 @@
                }
                total.sizes.Transfer(httpstate[i].sizes)
                total.headerSizes.Transfer(httpstate[i].headerSizes)
+               connectionStats.Transfer(connStats)
+       }
+       total.ConnectionStats = 
connectionStats.Export().CalcPercentiles(o.Percentiles)
+       if log.Log(log.Info) {
+               total.ConnectionStats.Print(out, "Connection time histogram 
(s)")
+       } else if log.Log(log.Warning) {
+               connectionStats.Counter.Print(out, "Connection time (s)")
        }
 
        // Sort the ip address form largest to smallest based on its usage count
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/fhttp/httprunner_test.go 
new/fortio-1.38.0/fhttp/httprunner_test.go
--- old/fortio-1.37.1/fhttp/httprunner_test.go  2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/fhttp/httprunner_test.go  2022-09-19 23:06:27.000000000 
+0200
@@ -58,7 +58,7 @@
        if totalReq != httpOk {
                t.Errorf("Mismatch between requests %d and ok %v", totalReq, 
res.RetCodes)
        }
-       if res.SocketCount != res.RunnerResults.NumThreads {
+       if res.SocketCount != int64(res.RunnerResults.NumThreads) {
                t.Errorf("%d socket used, expected same as thread# %d", 
res.SocketCount, res.RunnerResults.NumThreads)
        }
        count := getIPUsageCount(res.IPCountMap)
@@ -126,7 +126,7 @@
        if ngAfter > ngBefore2+8 {
                t.Errorf("Goroutines after test %d, expected it to stay near 
%d", ngAfter, ngBefore2)
        }
-       if res.SocketCount != res.RunnerResults.NumThreads {
+       if res.SocketCount != int64(res.RunnerResults.NumThreads) {
                t.Errorf("%d socket used, expected same as thread# %d", 
res.SocketCount, res.RunnerResults.NumThreads)
        }
 }
@@ -213,7 +213,7 @@
        if totalReq != httpOk {
                t.Errorf("Mismatch between requests %d and ok %v", totalReq, 
res.RetCodes)
        }
-       if int64(res.SocketCount) != numReq {
+       if res.SocketCount != numReq {
                t.Errorf("When closing, got %d while expected as many sockets 
as requests %d", res.SocketCount, numReq)
        }
 }
@@ -502,14 +502,14 @@
                        t.Error(err)
                }
 
-               if res.SocketCount != (int)(expectedSocketReuse) {
+               if res.SocketCount != (int64)(expectedSocketReuse) {
                        t.Errorf("Expecting %f socket to be used, got %d", 
expectedSocketReuse, res.SocketCount)
                }
        }
 
        // Test when connection reuse range min != max.
        // The actual socket count should always be 2 as the connection reuse 
range varies between 5 and 9.
-       expectedSocketReuse := 2
+       expectedSocketReuse := int64(2)
        opts.ConnReuseRange = [2]int{5, 9}
        // Check a few times that despite the range and random 2-9 we still 
always get 2 connections
        for i := 0; i < 5; i++ {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/periodic/periodic.go 
new/fortio-1.38.0/periodic/periodic.go
--- old/fortio-1.37.1/periodic/periodic.go      2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/periodic/periodic.go      2022-09-19 23:06:27.000000000 
+0200
@@ -105,12 +105,13 @@
                return
        }
        a.stopRequested = true
-       if a.hasStarted || !wait {
+       started := a.hasStarted
+       if started || !wait {
                log.LogVf("ABORT Closing %v", a)
                close(a.StopChan)
                a.StopChan = nil
                a.Unlock()
-               if a.hasStarted {
+               if started {
                        log.LogVf("ABORT reading start channel")
                        // shouldn't block/hang, just purging/resetting
                        <-a.StartChan
@@ -171,9 +172,11 @@
        // Note that this actually maps to gorountines and not actual threads
        // but threads seems like a more familiar name to use for non go users
        // and in a benchmarking context
-       NumThreads  int
+       NumThreads int
+       // List of percentiles to calculate.
        Percentiles []float64
-       Resolution  float64
+       // Divider to apply to duration data in seconds. Defaults to 0.001 or 1 
millisecond.
+       Resolution float64
        // Where to write the textual version of the results, defaults to stdout
        Out io.Writer `json:"-"`
        // Extra data to be copied back to the results (to be saved/JSON 
serialized)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/rapi/restHandler_test.go 
new/fortio-1.38.0/rapi/restHandler_test.go
--- old/fortio-1.37.1/rapi/restHandler_test.go  2022-09-13 19:31:18.000000000 
+0200
+++ new/fortio-1.38.0/rapi/restHandler_test.go  2022-09-19 23:06:27.000000000 
+0200
@@ -117,7 +117,7 @@
        if totalReq != httpOk {
                t.Errorf("Mismatch between requests %d and ok %v (%+v)", 
totalReq, res.RetCodes, res)
        }
-       if res.SocketCount != res.RunnerResults.NumThreads {
+       if res.SocketCount != int64(res.RunnerResults.NumThreads) {
                t.Errorf("%d socket used, expected same as thread# %d", 
res.SocketCount, res.RunnerResults.NumThreads)
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/fortio-1.37.1/stats/stats.go 
new/fortio-1.38.0/stats/stats.go
--- old/fortio-1.37.1/stats/stats.go    2022-09-13 19:31:18.000000000 +0200
+++ new/fortio-1.38.0/stats/stats.go    2022-09-19 23:06:27.000000000 +0200
@@ -196,7 +196,7 @@
        Avg         float64
        StdDev      float64
        Data        []Bucket
-       Percentiles []Percentile
+       Percentiles []Percentile `json:"Percentiles,omitempty"`
 }
 
 // NewHistogram creates a new histogram (sets up the buckets).

++++++ vendor.tar.gz ++++++

Reply via email to