mirror of https://github.com/ooni/probe-cli.git
Compare commits
4 Commits
7dab5a2981
...
51b185b55a
Author | SHA1 | Date |
---|---|---|
Simone Basso | 51b185b55a | |
Simone Basso | 6bd35a20be | |
Simone Basso | d12a3c2f3d | |
dependabot[bot] | d6d201d85e |
|
@ -31,7 +31,7 @@ jobs:
|
|||
- "1.20"
|
||||
- "1.21"
|
||||
- "1.22"
|
||||
system: [ubuntu-latest, macos-latest]
|
||||
system: [ubuntu-latest]
|
||||
runs-on: "${{ matrix.system }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
|
2
go.mod
2
go.mod
|
@ -41,7 +41,7 @@ require (
|
|||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/goptlib v1.5.0
|
||||
gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2 v2.6.1
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.22.0
|
||||
golang.org/x/net v0.23.0
|
||||
golang.org/x/sys v0.18.0
|
||||
)
|
||||
|
||||
|
|
4
go.sum
4
go.sum
|
@ -654,8 +654,8 @@ golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
|
|||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
|
@ -6,8 +6,10 @@ import (
|
|||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/ooni/netem"
|
||||
"github.com/ooni/probe-cli/v3/internal/randx"
|
||||
"github.com/ooni/probe-cli/v3/internal/runtimex"
|
||||
)
|
||||
|
||||
|
@ -173,14 +175,7 @@ func HTTPHandlerTimeout() http.Handler {
|
|||
}
|
||||
|
||||
func httpHandlerHijack(w http.ResponseWriter, r *http.Request, policy string) {
|
||||
// Note:
|
||||
//
|
||||
// 1. we assume we can hihack the connection
|
||||
//
|
||||
// 2. Hijack won't fail the first time it's invoked
|
||||
hijacker := w.(http.Hijacker)
|
||||
conn, _ := runtimex.Try2(hijacker.Hijack())
|
||||
|
||||
conn := httpHijack(w)
|
||||
defer conn.Close()
|
||||
|
||||
switch policy {
|
||||
|
@ -194,3 +189,40 @@ func httpHandlerHijack(w http.ResponseWriter, r *http.Request, policy string) {
|
|||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPHandlerResetWhileReadingBody returns a handler that sends a
|
||||
// connection reset by peer while the client is reading the body.
|
||||
func HTTPHandlerResetWhileReadingBody() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn := httpHijack(w)
|
||||
defer conn.Close()
|
||||
|
||||
// write the HTTP response headers
|
||||
conn.Write([]byte("HTTP/1.1 200 Ok\r\n"))
|
||||
conn.Write([]byte("Content-Type: text/html\r\n"))
|
||||
conn.Write([]byte("Content-Length: 65535\r\n"))
|
||||
conn.Write([]byte("\r\n"))
|
||||
|
||||
// start writing the response
|
||||
content := randx.Letters(32768)
|
||||
conn.Write([]byte(content))
|
||||
|
||||
// sleep for half a second simulating something wrong
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
// finally issue reset for the conn
|
||||
tcpMaybeResetNetConn(conn)
|
||||
})
|
||||
}
|
||||
|
||||
// httpHijack is a convenience function to hijack the underlying connection.
|
||||
func httpHijack(w http.ResponseWriter) net.Conn {
|
||||
// Note:
|
||||
//
|
||||
// 1. we assume we can hihack the connection
|
||||
//
|
||||
// 2. Hijack won't fail the first time it's invoked
|
||||
hijacker := w.(http.Hijacker)
|
||||
conn, _ := runtimex.Try2(hijacker.Hijack())
|
||||
return conn
|
||||
}
|
||||
|
|
|
@ -498,3 +498,43 @@ func TestHTTPTestxWithNetem(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPHandlerResetWhileReadingBody(t *testing.T) {
|
||||
// create a server for testing the given handler
|
||||
server := testingx.MustNewHTTPServer(testingx.HTTPHandlerResetWhileReadingBody())
|
||||
defer server.Close()
|
||||
|
||||
// create a suitable HTTP transport using netxlite
|
||||
netx := &netxlite.Netx{Underlying: nil}
|
||||
dialer := netx.NewDialerWithoutResolver(log.Log)
|
||||
handshaker := netx.NewTLSHandshakerStdlib(log.Log)
|
||||
tlsDialer := netxlite.NewTLSDialer(dialer, handshaker)
|
||||
txp := netxlite.NewHTTPTransportWithOptions(log.Log, dialer, tlsDialer)
|
||||
|
||||
// create the request
|
||||
req := runtimex.Try1(http.NewRequest("GET", server.URL, nil))
|
||||
|
||||
// perform the round trip
|
||||
resp, err := txp.RoundTrip(req)
|
||||
|
||||
// we do not expect an error during the round trip
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// make sure we close the body
|
||||
defer resp.Body.Close()
|
||||
|
||||
// start reading the response where we expect to see a RST
|
||||
respbody, err := netxlite.ReadAllContext(req.Context(), resp.Body)
|
||||
|
||||
// verify we received a connection reset
|
||||
if !errors.Is(err, netxlite.ECONNRESET) {
|
||||
t.Fatal("expected ECONNRESET, got", err)
|
||||
}
|
||||
|
||||
// make sure we've got no bytes
|
||||
if len(respbody) != 0 {
|
||||
t.Fatal("expected to see zero bytes here")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package testingx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/logmodel"
|
||||
)
|
||||
|
||||
// Logger implements [logmodel.Logger] and collects all the log lines.
|
||||
//
|
||||
// The zero value of this struct is ready to use.
|
||||
type Logger struct {
|
||||
// debug contains debug lines.
|
||||
debug []string
|
||||
|
||||
// info contains info lines.
|
||||
info []string
|
||||
|
||||
// mu provides mutual exclusion.
|
||||
mu sync.Mutex
|
||||
|
||||
// warning contains warning lines.
|
||||
warning []string
|
||||
}
|
||||
|
||||
var _ logmodel.Logger = &Logger{}
|
||||
|
||||
// Debug implements logmodel.Logger.
|
||||
func (l *Logger) Debug(msg string) {
|
||||
l.mu.Lock()
|
||||
l.debug = append(l.debug, msg)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// Debugf implements logmodel.Logger.
|
||||
func (l *Logger) Debugf(format string, v ...interface{}) {
|
||||
l.Debug(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Info implements logmodel.Logger.
|
||||
func (l *Logger) Info(msg string) {
|
||||
l.mu.Lock()
|
||||
l.info = append(l.info, msg)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// Infof implements logmodel.Logger.
|
||||
func (l *Logger) Infof(format string, v ...interface{}) {
|
||||
l.Info(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Warn implements logmodel.Logger.
|
||||
func (l *Logger) Warn(msg string) {
|
||||
l.mu.Lock()
|
||||
l.warning = append(l.warning, msg)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// Warnf implements logmodel.Logger.
|
||||
func (l *Logger) Warnf(format string, v ...interface{}) {
|
||||
l.Warn(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// DebugLines returns a copy of the observed debug lines.
|
||||
func (l *Logger) DebugLines() []string {
|
||||
l.mu.Lock()
|
||||
out := append([]string{}, l.debug...)
|
||||
l.mu.Unlock()
|
||||
return out
|
||||
}
|
||||
|
||||
// InfoLines returns a copy of the observed info lines.
|
||||
func (l *Logger) InfoLines() []string {
|
||||
l.mu.Lock()
|
||||
out := append([]string{}, l.info...)
|
||||
l.mu.Unlock()
|
||||
return out
|
||||
}
|
||||
|
||||
// WarnLines returns a copy of the observed warn lines.
|
||||
func (l *Logger) WarnLines() []string {
|
||||
l.mu.Lock()
|
||||
out := append([]string{}, l.warning...)
|
||||
l.mu.Unlock()
|
||||
return out
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package testingx
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
logger := &Logger{}
|
||||
|
||||
logger.Debug("foobar")
|
||||
logger.Debugf("foo%s", "baz")
|
||||
expectDebug := []string{"foobar", "foobaz"}
|
||||
|
||||
logger.Info("barfoo")
|
||||
logger.Infof("bar%s", "baz")
|
||||
expectInfo := []string{"barfoo", "barbaz"}
|
||||
|
||||
logger.Warn("jarjar")
|
||||
logger.Warnf("jar%s", "baz")
|
||||
expectWarn := []string{"jarjar", "jarbaz"}
|
||||
|
||||
if diff := cmp.Diff(expectDebug, logger.DebugLines()); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
if diff := cmp.Diff(expectInfo, logger.InfoLines()); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
if diff := cmp.Diff(expectWarn, logger.WarnLines()); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue