// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "go/ast" "go/token" ) var httpserverFix = fix{ "httpserver", httpserver, `Adapt http server methods and functions to changes made to the http ResponseWriter interface. http://codereview.appspot.com/4245064 Hijacker http://codereview.appspot.com/4239076 Header http://codereview.appspot.com/4239077 Flusher http://codereview.appspot.com/4248075 RemoteAddr, UsingTLS `, } func init() { register(httpserverFix) } func httpserver(f *ast.File) bool { if !imports(f, "http") { return false } fixed := false for _, decl := range f.Decls { fn, ok := decl.(*ast.FuncDecl) if !ok { continue } w, req, ok := isServeHTTP(fn) if !ok { continue } rewrite(fn.Body, func(n interface{}) { // Want to replace expression sometimes, // so record pointer to it for updating below. ptr, ok := n.(*ast.Expr) if ok { n = *ptr } // Look for w.UsingTLS() and w.Remoteaddr(). call, ok := n.(*ast.CallExpr) if !ok || (len(call.Args) != 0 && len(call.Args) != 2) { return } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return } if !refersTo(sel.X, w) { return } switch sel.Sel.String() { case "Hijack": // replace w with w.(http.Hijacker) sel.X = &ast.TypeAssertExpr{ X: sel.X, Type: ast.NewIdent("http.Hijacker"), } fixed = true case "Flush": // replace w with w.(http.Flusher) sel.X = &ast.TypeAssertExpr{ X: sel.X, Type: ast.NewIdent("http.Flusher"), } fixed = true case "UsingTLS": if ptr == nil { // can only replace expression if we have pointer to it break } // replace with req.TLS != nil *ptr = &ast.BinaryExpr{ X: &ast.SelectorExpr{ X: ast.NewIdent(req.String()), Sel: ast.NewIdent("TLS"), }, Op: token.NEQ, Y: ast.NewIdent("nil"), } fixed = true case "RemoteAddr": if ptr == nil { // can only replace expression if we have pointer to it break } // replace with req.RemoteAddr *ptr = &ast.SelectorExpr{ X: ast.NewIdent(req.String()), Sel: ast.NewIdent("RemoteAddr"), } fixed = true case "SetHeader": // replace w.SetHeader with w.Header().Set // or w.Header().Del if second argument is "" sel.X = &ast.CallExpr{ Fun: &ast.SelectorExpr{ X: ast.NewIdent(w.String()), Sel: ast.NewIdent("Header"), }, } sel.Sel = ast.NewIdent("Set") if len(call.Args) == 2 && isEmptyString(call.Args[1]) { sel.Sel = ast.NewIdent("Del") call.Args = call.Args[:1] } fixed = true } }) } return fixed } func isServeHTTP(fn *ast.FuncDecl) (w, req *ast.Ident, ok bool) { for _, field := range fn.Type.Params.List { if isPkgDot(field.Type, "http", "ResponseWriter") { w = field.Names[0] continue } if isPtrPkgDot(field.Type, "http", "Request") { req = field.Names[0] continue } } ok = w != nil && req != nil return }