虽然是个小坑,可是trace了一个星期,浪费了不少时间,解决方案很简单,思考的过程却是很值得借鉴:)
http web完整代码在:https://github.com/philsong/golang_samples/tree/master/src/emvdecoder
emvdecoder中问题代码片段如下:
func checkError(w http.ResponseWriter,err error) { if err != nil { fmt.Fprintf(w,"Fatal error ",err.Error()) os.Exit(1) } } func index(w http.ResponseWriter,r *http.Request) { r.ParseForm() t,err := template.ParseFiles("index.html") checkError(w,err) err = t.Execute(w,emvdecoder) checkError(w,err) }
于是自己就搜索google,发现了这篇文章,Linux 技巧:让进程在后台可靠运行的几种方法http://www.jb51.cc/article/p-nmsbedla-bnx.html
用nohupemvdecoder&运行后,似乎运行良好了(稍后就知道这是巧合。。。),于是发一微博
继续测试发现其实无论用&, nohup还是setsid, 父进程已经变成了init了,所以效果其实一样,于是猜测是代码的bug(其实又是自己的问题...),但是奇怪为什么没打印出来呢
philsong@compiler:~$ cd go/src/emvdecoder/
philsong@compiler:~/go/src/emvdecoder$ emvdecoder&
[1] 22908
philsong@compiler:~/go/src/emvdecoder$ ps
PID TTY TIME CMD
22729 pts/2 00:00:01 bash
22908 pts/2 00:00:00 emvdecoder
22911 pts/2 00:00:00 ps
philsong@compiler:~/go/src/emvdecoder$ pstree -ps 22908
init(1)---sshd(702)---sshd(22703)---sshd(22728)---bash(22729)---emvdecoder(22908)-+-{emvdecoder}(22909)
`-{emvdecoder}(22910)
断开ssh后,
philsong@compiler:~$ pstree -ps 22908
init(1)---emvdecoder(22908)-+-{emvdecoder}(22909)
|-{emvdecoder}(22910)
|-{emvdecoder}(22916)
`-{emvdecoder}(22917)
,
fuck代码发现
func checkError(w http.ResponseWriter,err.Error()) os.Exit(1) } }
fmt.Fprintf(w由于copy时默认打印到http 的w句柄,并没有打印道控制台,囧啊,改为
fmt.Println("
继续log大法,并打印堆栈
import "runtime/debug"
debug.PrintStack()
func checkError(w http.ResponseWriter,err error) { fmt.Println("error: ") if err != nil { fmt.Println("Fatal error ",err.Error()) debug.PrintStack() os.Exit(1) } } func index(w http.ResponseWriter,r *http.Request) { println("Request ",r.URL.Path," from ",r.RemoteAddr) // path := r.URL.Path[1:] path := "." + r.URL.Path fmt.Println("path",path) if path == "./favicon.ico" { http.NotFound(w,r) return } r.ParseForm() t,err) }
这次捕获到珍贵的输出:
Request / from 208.94.145.109:22492
path ./
error:
error:
Fatal error template: index.html:1:0: executing "index.html" at <"<!DOCTYPE HTML>\r\n...>: http: request method or response status code does not allow body
/home/philsong/go/src/emvdecoder/emvdecoder.go:47 (0x8048d50)
checkError: debug.PrintStack()
/home/philsong/go/src/emvdecoder/emvdecoder.go:69 (0x80490a5)
index: checkError(w,err)
/usr/local/go/src/pkg/net/http/server.go:1149 (0x809936c)
HandlerFunc.ServeHTTP: f(w,r)
/usr/local/go/src/pkg/net/http/server.go:1416 (0x809a39d)
(*ServeMux).ServeHTTP: h.ServeHTTP(w,r)
/usr/local/go/src/pkg/net/http/server.go:1517 (0x809aa21)
serverHandler.ServeHTTP: handler.ServeHTTP(rw,req)
/usr/local/go/src/pkg/net/http/server.go:1096 (0x8099073)
(*conn).serve: serverHandler{c.server}.ServeHTTP(w,w.req)
/usr/local/go/src/pkg/runtime/proc.c:1223 (0x805fd60)
goexit: runtime路goexit(void)
The error is self-explaining:
request method or response status code does not allow body
A HEAD request only allows HTTP headers to be sent back as response.
899 func (w *response) Write(data []byte) (n int,err error) { 900 if w.conn.hijacked() { 901 log.Print("http: response.Write on hijacked connection") 902 return 0,ErrHijacked 903 } 904 if !w.wroteHeader { 905 w.WriteHeader(StatusOK) 906 } 907 if len(data) == 0 { 908 return 0,nil 909 } 910 if !w.bodyAllowed() { 911 return 0,ErrBodyNotAllowed 912 } 913 914 w.written += int64(len(data)) // ignoring errors,for errorKludge 915 if w.contentLength != -1 && w.written > w.contentLength { 916 return 0,ErrContentLength 917 } 918 return w.w.Write(data) 919 }猜测是发起的head请求,不是正常的get和post请求,怪不得,直接用浏览器访问时无法重现这个bug,于是用
curl: (52) Empty reply from server
//os.Exit(1)
HTTP/1.1 200 OK
Date: Tue,17 Sep 2013 06:31:32 GMT
philsong@compiler:~$ curl -I 192.168.7.14:9090
HTTP/1.1 200 OK
Date: Tue,17 Sep 2013 06:31:40 GMT