libgo: update to go1.8rc3

Reviewed-on: https://go-review.googlesource.com/35844

From-SVN: r244981
This commit is contained in:
Ian Lance Taylor 2017-01-27 15:01:57 +00:00
parent 3f54004b09
commit 42f20102ef
14 changed files with 389 additions and 92 deletions

View File

@ -1,4 +1,4 @@
59f181b6fda68ece22882945853ca2df9dbf1c88
2a5f65a98ca483aad2dd74dc2636a7baecc59cf2
The first line of this file holds the git revision number of the
last merge done from the master library sources.

View File

@ -1 +1 @@
go1.8rc2
go1.8rc3

View File

@ -3787,3 +3787,26 @@ GLOBL ·constants<>(SB),8,$8
tg.setenv("GOPATH", tg.path("go"))
tg.run("build", "p")
}
// Issue 18778.
func TestDotDotDotOutsideGOPATH(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("pkgs/a.go", `package x`)
tg.tempFile("pkgs/a_test.go", `package x_test
import "testing"
func TestX(t *testing.T) {}`)
tg.tempFile("pkgs/a/a.go", `package a`)
tg.tempFile("pkgs/a/a_test.go", `package a_test
import "testing"
func TestA(t *testing.T) {}`)
tg.cd(tg.path("pkgs"))
tg.run("build", "./...")
tg.run("test", "./...")
tg.run("list", "./...")
tg.grepStdout("pkgs$", "expected package not listed")
tg.grepStdout("pkgs/a", "expected package not listed")
}

View File

@ -433,7 +433,7 @@ func setErrorPos(p *Package, importPos []token.Position) *Package {
func cleanImport(path string) string {
orig := path
path = pathpkg.Clean(path)
if strings.HasPrefix(orig, "./") && path != ".." && path != "." && !strings.HasPrefix(path, "../") {
if strings.HasPrefix(orig, "./") && path != ".." && !strings.HasPrefix(path, "../") {
path = "./" + path
}
return path

View File

@ -1357,16 +1357,7 @@ func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStra
cancel: cancel,
ctx: ctx,
}
go func(tx *Tx) {
select {
case <-tx.ctx.Done():
if !tx.isDone() {
// Discard and close the connection used to ensure the transaction
// is closed and the resources are released.
tx.rollback(true)
}
}
}(tx)
go tx.awaitDone()
return tx, nil
}
@ -1388,6 +1379,11 @@ func (db *DB) Driver() driver.Driver {
type Tx struct {
db *DB
// closemu prevents the transaction from closing while there
// is an active query. It is held for read during queries
// and exclusively during close.
closemu sync.RWMutex
// dc is owned exclusively until Commit or Rollback, at which point
// it's returned with putConn.
dc *driverConn
@ -1413,6 +1409,20 @@ type Tx struct {
ctx context.Context
}
// awaitDone blocks until the context in Tx is canceled and rolls back
// the transaction if it's not already done.
func (tx *Tx) awaitDone() {
// Wait for either the transaction to be committed or rolled
// back, or for the associated context to be closed.
<-tx.ctx.Done()
// Discard and close the connection used to ensure the
// transaction is closed and the resources are released. This
// rollback does nothing if the transaction has already been
// committed or rolled back.
tx.rollback(true)
}
func (tx *Tx) isDone() bool {
return atomic.LoadInt32(&tx.done) != 0
}
@ -1424,16 +1434,31 @@ var ErrTxDone = errors.New("sql: Transaction has already been committed or rolle
// close returns the connection to the pool and
// must only be called by Tx.rollback or Tx.Commit.
func (tx *Tx) close(err error) {
tx.closemu.Lock()
defer tx.closemu.Unlock()
tx.db.putConn(tx.dc, err)
tx.cancel()
tx.dc = nil
tx.txi = nil
}
// hookTxGrabConn specifies an optional hook to be called on
// a successful call to (*Tx).grabConn. For tests.
var hookTxGrabConn func()
func (tx *Tx) grabConn(ctx context.Context) (*driverConn, error) {
select {
default:
case <-ctx.Done():
return nil, ctx.Err()
}
if tx.isDone() {
return nil, ErrTxDone
}
if hookTxGrabConn != nil { // test hook
hookTxGrabConn()
}
return tx.dc, nil
}
@ -1503,6 +1528,9 @@ func (tx *Tx) Rollback() error {
// for the execution of the returned statement. The returned statement
// will run in the transaction context.
func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error) {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
// TODO(bradfitz): We could be more efficient here and either
// provide a method to take an existing Stmt (created on
// perhaps a different Conn), and re-create it on this Conn if
@ -1567,6 +1595,9 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
// The returned statement operates within the transaction and will be closed
// when the transaction has been committed or rolled back.
func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
// TODO(bradfitz): optimize this. Currently this re-prepares
// each time. This is fine for now to illustrate the API but
// we should really cache already-prepared statements
@ -1618,6 +1649,9 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
// ExecContext executes a query that doesn't return rows.
// For example: an INSERT and UPDATE.
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error) {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
dc, err := tx.grabConn(ctx)
if err != nil {
return nil, err
@ -1661,6 +1695,9 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
// QueryContext executes a query that returns rows, typically a SELECT.
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error) {
tx.closemu.RLock()
defer tx.closemu.RUnlock()
dc, err := tx.grabConn(ctx)
if err != nil {
return nil, err
@ -2038,25 +2075,21 @@ type Rows struct {
// closed value is 1 when the Rows is closed.
// Use atomic operations on value when checking value.
closed int32
ctxClose chan struct{} // closed when Rows is closed, may be null.
cancel func() // called when Rows is closed, may be nil.
lastcols []driver.Value
lasterr error // non-nil only if closed is true
closeStmt *driverStmt // if non-nil, statement to Close on close
}
func (rs *Rows) initContextClose(ctx context.Context) {
if ctx.Done() == context.Background().Done() {
return
}
ctx, rs.cancel = context.WithCancel(ctx)
go rs.awaitDone(ctx)
}
rs.ctxClose = make(chan struct{})
go func() {
select {
case <-ctx.Done():
rs.Close()
case <-rs.ctxClose:
}
}()
// awaitDone blocks until the rows are closed or the context canceled.
func (rs *Rows) awaitDone(ctx context.Context) {
<-ctx.Done()
rs.Close()
}
// Next prepares the next result row for reading with the Scan method. It
@ -2314,7 +2347,9 @@ func (rs *Rows) Scan(dest ...interface{}) error {
return nil
}
var rowsCloseHook func(*Rows, *error)
// rowsCloseHook returns a function so tests may install the
// hook throug a test only mutex.
var rowsCloseHook = func() func(*Rows, *error) { return nil }
func (rs *Rows) isClosed() bool {
return atomic.LoadInt32(&rs.closed) != 0
@ -2328,13 +2363,15 @@ func (rs *Rows) Close() error {
if !atomic.CompareAndSwapInt32(&rs.closed, 0, 1) {
return nil
}
if rs.ctxClose != nil {
close(rs.ctxClose)
}
err := rs.rowsi.Close()
if fn := rowsCloseHook; fn != nil {
if fn := rowsCloseHook(); fn != nil {
fn(rs, &err)
}
if rs.cancel != nil {
rs.cancel()
}
if rs.closeStmt != nil {
rs.closeStmt.Close()
}

View File

@ -14,6 +14,7 @@ import (
"runtime"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
)
@ -326,9 +327,7 @@ func TestQueryContext(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
if n := db.numFreeConns(); n != 1 {
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
}
waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
@ -345,6 +344,18 @@ func waitCondition(waitFor, checkEvery time.Duration, fn func() bool) bool {
return false
}
// waitForFree checks db.numFreeConns until either it equals want or
// the maxWait time elapses.
func waitForFree(t *testing.T, db *DB, maxWait time.Duration, want int) {
var numFree int
if !waitCondition(maxWait, 5*time.Millisecond, func() bool {
numFree = db.numFreeConns()
return numFree == want
}) {
t.Fatalf("free conns after hitting EOF = %d; want %d", numFree, want)
}
}
func TestQueryContextWait(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
@ -361,9 +372,7 @@ func TestQueryContextWait(t *testing.T) {
}
// Verify closed rows connection after error condition.
if n := db.numFreeConns(); n != 1 {
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
}
waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
@ -388,13 +397,7 @@ func TestTxContextWait(t *testing.T) {
t.Fatalf("expected QueryContext to error with context deadline exceeded but returned %v", err)
}
var numFree int
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
numFree = db.numFreeConns()
return numFree == 0
}) {
t.Fatalf("free conns after hitting EOF = %d; want 0", numFree)
}
waitForFree(t, db, 5*time.Second, 0)
// Ensure the dropped connection allows more connections to be made.
// Checked on DB Close.
@ -471,9 +474,7 @@ func TestMultiResultSetQuery(t *testing.T) {
// And verify that the final rows.Next() call, which hit EOF,
// also closed the rows connection.
if n := db.numFreeConns(); n != 1 {
t.Fatalf("free conns after query hitting EOF = %d; want 1", n)
}
waitForFree(t, db, 5*time.Second, 1)
if prepares := numPrepares(t, db) - prepares0; prepares != 1 {
t.Errorf("executed %d Prepare statements; want 1", prepares)
}
@ -1135,6 +1136,24 @@ func TestQueryRowClosingStmt(t *testing.T) {
}
}
var atomicRowsCloseHook atomic.Value // of func(*Rows, *error)
func init() {
rowsCloseHook = func() func(*Rows, *error) {
fn, _ := atomicRowsCloseHook.Load().(func(*Rows, *error))
return fn
}
}
func setRowsCloseHook(fn func(*Rows, *error)) {
if fn == nil {
// Can't change an atomic.Value back to nil, so set it to this
// no-op func instead.
fn = func(*Rows, *error) {}
}
atomicRowsCloseHook.Store(fn)
}
// Test issue 6651
func TestIssue6651(t *testing.T) {
db := newTestDB(t, "people")
@ -1147,6 +1166,7 @@ func TestIssue6651(t *testing.T) {
return fmt.Errorf(want)
}
defer func() { rowsCursorNextHook = nil }()
err := db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want)
@ -1154,10 +1174,10 @@ func TestIssue6651(t *testing.T) {
rowsCursorNextHook = nil
want = "error in rows.Close"
rowsCloseHook = func(rows *Rows, err *error) {
setRowsCloseHook(func(rows *Rows, err *error) {
*err = fmt.Errorf(want)
}
defer func() { rowsCloseHook = nil }()
})
defer setRowsCloseHook(nil)
err = db.QueryRow("SELECT|people|name|").Scan(&v)
if err == nil || err.Error() != want {
t.Errorf("error = %q; want %q", err, want)
@ -1830,7 +1850,9 @@ func TestStmtCloseDeps(t *testing.T) {
db.dumpDeps(t)
}
if len(stmt.css) > nquery {
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
return len(stmt.css) <= nquery
}) {
t.Errorf("len(stmt.css) = %d; want <= %d", len(stmt.css), nquery)
}
@ -2576,10 +2598,10 @@ func TestIssue6081(t *testing.T) {
if err != nil {
t.Fatal(err)
}
rowsCloseHook = func(rows *Rows, err *error) {
setRowsCloseHook(func(rows *Rows, err *error) {
*err = driver.ErrBadConn
}
defer func() { rowsCloseHook = nil }()
})
defer setRowsCloseHook(nil)
for i := 0; i < 10; i++ {
rows, err := stmt.Query()
if err != nil {
@ -2642,7 +2664,10 @@ func TestIssue18429(t *testing.T) {
if err != nil {
return
}
rows, err := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
// This is expected to give a cancel error many, but not all the time.
// Test failure will happen with a panic or other race condition being
// reported.
rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
if rows != nil {
rows.Close()
}
@ -2655,6 +2680,56 @@ func TestIssue18429(t *testing.T) {
time.Sleep(milliWait * 3 * time.Millisecond)
}
// TestIssue18719 closes the context right before use. The sql.driverConn
// will nil out the ci on close in a lock, but if another process uses it right after
// it will panic with on the nil ref.
//
// See https://golang.org/cl/35550 .
func TestIssue18719(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tx, err := db.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
hookTxGrabConn = func() {
cancel()
// Wait for the context to cancel and tx to rollback.
for tx.isDone() == false {
time.Sleep(time.Millisecond * 3)
}
}
defer func() { hookTxGrabConn = nil }()
// This call will grab the connection and cancel the context
// after it has done so. Code after must deal with the canceled state.
rows, err := tx.QueryContext(ctx, "SELECT|people|name|")
if err != nil {
rows.Close()
t.Fatalf("expected error %v but got %v", nil, err)
}
// Rows may be ignored because it will be closed when the context is canceled.
// Do not explicitly rollback. The rollback will happen from the
// canceled context.
// Wait for connections to return to pool.
var numOpen int
if !waitCondition(5*time.Second, 5*time.Millisecond, func() bool {
numOpen = db.numOpenConns()
return numOpen == 0
}) {
t.Fatalf("open conns after hitting EOF = %d; want 0", numOpen)
}
}
func TestConcurrency(t *testing.T) {
doConcurrentTest(t, new(concurrentDBQueryTest))
doConcurrentTest(t, new(concurrentDBExecTest))

View File

@ -733,7 +733,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.FuncLit:
p.expr(x.Type)
p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
p.funcBody(p.distanceFrom(x.Type.Pos()), blank, x.Body)
case *ast.ParenExpr:
if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
@ -825,6 +825,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
if x.Type != nil {
p.expr1(x.Type, token.HighestPrec, depth)
}
p.level++
p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
// do not insert extra line break following a /*-style comment
@ -837,6 +838,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
mode |= noExtraBlank
}
p.print(mode, x.Rbrace, token.RBRACE, mode)
p.level--
case *ast.Ellipsis:
p.print(token.ELLIPSIS)
@ -1557,18 +1559,23 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
return bodySize
}
// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
// a header (e.g., a for-loop control clause or function signature) of given headerSize.
// funcBody prints a function body following a function header of given headerSize.
// If the header's and block's size are "small enough" and the block is "simple enough",
// the block is printed on the current line, without line breaks, spaced from the header
// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
// lines for the block's statements and its closing "}".
//
func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
if b == nil {
return
}
// save/restore composite literal nesting level
defer func(level int) {
p.level = level
}(p.level)
p.level = 0
const maxSize = 100
if headerSize+p.bodySize(b, maxSize) <= maxSize {
p.print(sep, b.Lbrace, token.LBRACE)
@ -1613,7 +1620,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
}
p.expr(d.Name)
p.signature(d.Type.Params, d.Type.Results)
p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
p.funcBody(p.distanceFrom(d.Pos()), vtab, d.Body)
}
func (p *printer) decl(decl ast.Decl) {

View File

@ -58,6 +58,7 @@ type printer struct {
// Current state
output []byte // raw printer result
indent int // current indentation
level int // level == 0: outside composite literal; level > 0: inside composite literal
mode pmode // current printer mode
impliedSemi bool // if set, a linebreak implies a semicolon
lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace)
@ -744,15 +745,19 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
// follows on the same line but is not a comma, and not a "closing"
// token immediately following its corresponding "opening" token,
// add an extra separator unless explicitly disabled. Use a blank
// as separator unless we have pending linebreaks and they are not
// disabled, in which case we want a linebreak (issue 15137).
// as separator unless we have pending linebreaks, they are not
// disabled, and we are outside a composite literal, in which case
// we want a linebreak (issue 15137).
// TODO(gri) This has become overly complicated. We should be able
// to track whether we're inside an expression or statement and
// use that information to decide more directly.
needsLinebreak := false
if p.mode&noExtraBlank == 0 &&
last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
tok != token.COMMA &&
(tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
(tok != token.RBRACK || p.prevOpen == token.LBRACK) {
if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 {
if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 {
needsLinebreak = true
} else {
p.writeByte(' ', 1)

View File

@ -103,3 +103,62 @@ label:
mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations
}
// Test cases for issue 18782
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
var _ = T{ /* a */ 0}
var _ = T{ /* a */ /* b */ 0}
var _ = T{ /* a */ /* b */
/* c */ 0,
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ 0,
}
var _ = T{
/* a */
/* b */ 0,
}
var _ = T{ /* a */ {}}
var _ = T{ /* a */ /* b */ {}}
var _ = T{ /* a */ /* b */
/* c */ {},
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ {},
}
var _ = T{
/* a */
/* b */ {},
}
var _ = []T{
func() {
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
},
}

View File

@ -103,3 +103,66 @@ label:
mask := uint64(1)<<c - 1 // Allocation mask
used := atomic.LoadUint64(&h.used) // Current allocations
}
// Test cases for issue 18782
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
var _ = T{ /* a */ 0,
}
var _ = T{ /* a */ /* b */ 0,
}
var _ = T{ /* a */ /* b */
/* c */ 0,
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ 0,
}
var _ = T{
/* a */
/* b */ 0,
}
var _ = T{ /* a */ {},
}
var _ = T{ /* a */ /* b */ {},
}
var _ = T{ /* a */ /* b */
/* c */ {},
}
var _ = T{ /* a */ /* b */
/* c */
/* d */ {},
}
var _ = T{
/* a */
/* b */ {},
}
var _ = []T{
func() {
var _ = [][]int{
/* a, b, c, d, e */
/* a */ {0, 0, 0, 0, 0},
/* b */ {0, 5, 4, 4, 4},
/* c */ {0, 4, 5, 4, 4},
/* d */ {0, 4, 4, 5, 4},
/* e */ {0, 4, 4, 4, 5},
}
},
}

View File

@ -413,11 +413,12 @@ func (c *Client) checkRedirect(req *Request, via []*Request) error {
// redirectBehavior describes what should happen when the
// client encounters a 3xx status code from the server
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect bool) {
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
switch resp.StatusCode {
case 301, 302, 303:
redirectMethod = reqMethod
shouldRedirect = true
includeBody = false
// RFC 2616 allowed automatic redirection only with GET and
// HEAD requests. RFC 7231 lifts this restriction, but we still
@ -429,6 +430,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect
case 307, 308:
redirectMethod = reqMethod
shouldRedirect = true
includeBody = true
// Treat 307 and 308 specially, since they're new in
// Go 1.8, and they also require re-sending the request body.
@ -449,7 +451,7 @@ func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirect
shouldRedirect = false
}
}
return redirectMethod, shouldRedirect
return redirectMethod, shouldRedirect, includeBody
}
// Do sends an HTTP request and returns an HTTP response, following
@ -492,11 +494,14 @@ func (c *Client) Do(req *Request) (*Response, error) {
}
var (
deadline = c.deadline()
reqs []*Request
resp *Response
copyHeaders = c.makeHeadersCopier(req)
deadline = c.deadline()
reqs []*Request
resp *Response
copyHeaders = c.makeHeadersCopier(req)
// Redirect behavior:
redirectMethod string
includeBody bool
)
uerr := func(err error) error {
req.closeBody()
@ -534,7 +539,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
Cancel: ireq.Cancel,
ctx: ireq.ctx,
}
if ireq.GetBody != nil {
if includeBody && ireq.GetBody != nil {
req.Body, err = ireq.GetBody()
if err != nil {
return nil, uerr(err)
@ -598,7 +603,7 @@ func (c *Client) Do(req *Request) (*Response, error) {
}
var shouldRedirect bool
redirectMethod, shouldRedirect = redirectBehavior(req.Method, resp, reqs[0])
redirectMethod, shouldRedirect, includeBody = redirectBehavior(req.Method, resp, reqs[0])
if !shouldRedirect {
return resp, nil
}

View File

@ -360,25 +360,25 @@ func TestPostRedirects(t *testing.T) {
wantSegments := []string{
`POST / "first"`,
`POST /?code=301&next=302 "c301"`,
`GET /?code=302 "c301"`,
`GET / "c301"`,
`GET /?code=302 ""`,
`GET / ""`,
`POST /?code=302&next=302 "c302"`,
`GET /?code=302 "c302"`,
`GET / "c302"`,
`GET /?code=302 ""`,
`GET / ""`,
`POST /?code=303&next=301 "c303wc301"`,
`GET /?code=301 "c303wc301"`,
`GET / "c303wc301"`,
`GET /?code=301 ""`,
`GET / ""`,
`POST /?code=304 "c304"`,
`POST /?code=305 "c305"`,
`POST /?code=307&next=303,308,302 "c307"`,
`POST /?code=303&next=308,302 "c307"`,
`GET /?code=308&next=302 "c307"`,
`GET /?code=308&next=302 ""`,
`GET /?code=302 "c307"`,
`GET / "c307"`,
`GET / ""`,
`POST /?code=308&next=302,301 "c308"`,
`POST /?code=302&next=301 "c308"`,
`GET /?code=301 "c308"`,
`GET / "c308"`,
`GET /?code=301 ""`,
`GET / ""`,
`POST /?code=404 "c404"`,
}
want := strings.Join(wantSegments, "\n")
@ -399,20 +399,20 @@ func TestDeleteRedirects(t *testing.T) {
wantSegments := []string{
`DELETE / "first"`,
`DELETE /?code=301&next=302,308 "c301"`,
`GET /?code=302&next=308 "c301"`,
`GET /?code=308 "c301"`,
`GET /?code=302&next=308 ""`,
`GET /?code=308 ""`,
`GET / "c301"`,
`DELETE /?code=302&next=302 "c302"`,
`GET /?code=302 "c302"`,
`GET / "c302"`,
`GET /?code=302 ""`,
`GET / ""`,
`DELETE /?code=303 "c303"`,
`GET / "c303"`,
`GET / ""`,
`DELETE /?code=307&next=301,308,303,302,304 "c307"`,
`DELETE /?code=301&next=308,303,302,304 "c307"`,
`GET /?code=308&next=303,302,304 "c307"`,
`GET /?code=308&next=303,302,304 ""`,
`GET /?code=303&next=302,304 "c307"`,
`GET /?code=302&next=304 "c307"`,
`GET /?code=304 "c307"`,
`GET /?code=302&next=304 ""`,
`GET /?code=304 ""`,
`DELETE /?code=308&next=307 "c308"`,
`DELETE /?code=307 "c308"`,
`DELETE / "c308"`,
@ -432,7 +432,11 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
log.Lock()
slurp, _ := ioutil.ReadAll(r.Body)
fmt.Fprintf(&log.Buffer, "%s %s %q\n", r.Method, r.RequestURI, slurp)
fmt.Fprintf(&log.Buffer, "%s %s %q", r.Method, r.RequestURI, slurp)
if cl := r.Header.Get("Content-Length"); r.Method == "GET" && len(slurp) == 0 && (r.ContentLength != 0 || cl != "") {
fmt.Fprintf(&log.Buffer, " (but with body=%T, content-length = %v, %q)", r.Body, r.ContentLength, cl)
}
log.WriteByte('\n')
log.Unlock()
urlQuery := r.URL.Query()
if v := urlQuery.Get("code"); v != "" {
@ -475,7 +479,24 @@ func testRedirectsByMethod(t *testing.T, method string, table []redirectTest, wa
want = strings.TrimSpace(want)
if got != want {
t.Errorf("Log differs.\n Got:\n%s\nWant:\n%s\n", got, want)
got, want, lines := removeCommonLines(got, want)
t.Errorf("Log differs after %d common lines.\n\nGot:\n%s\n\nWant:\n%s\n", lines, got, want)
}
}
func removeCommonLines(a, b string) (asuffix, bsuffix string, commonLines int) {
for {
nl := strings.IndexByte(a, '\n')
if nl < 0 {
return a, b, commonLines
}
line := a[:nl+1]
if !strings.HasPrefix(b, line) {
return a, b, commonLines
}
commonLines++
a = a[len(line):]
b = b[len(line):]
}
}

View File

@ -5277,7 +5277,7 @@ func TestServerHijackGetsBackgroundByte_big(t *testing.T) {
defer conn.Close()
slurp, err := ioutil.ReadAll(buf.Reader)
if err != nil {
t.Error("Copy: %v", err)
t.Errorf("Copy: %v", err)
}
allX := true
for _, v := range slurp {

View File

@ -71,7 +71,9 @@ merge() {
elif test -f ${old}; then
# The file exists in the old version.
if ! test -f ${libgo}; then
echo "merge.sh: $name: skipping: exists in old and new git, but not in libgo"
if ! cmp -s ${old} ${new}; then
echo "merge.sh: $name: skipping: exists in old and new git, but not in libgo"
fi
continue
fi
if cmp -s ${old} ${libgo}; then