golang server.go 处理head请求的一个小坑

虽然是个小坑,可是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)
}


问题出现了: 刚开始用ssh登陆到ubuntu,然后运行emvdecoder&后,断开ssh,然后过一会就发现web已经连不上了。。。怀疑是输入输出的问题,猜测程序可能被信号中断之类的,于是微博发一信息请教牛人:“ 请教你个问题,我有个程序xxx,用ssh登录到linux,然后运行xxx&跑到后台,可以通过 http://t.cn/z8oSEpF  访问,然后断开ssh之后,这个xxx就好像挂掉了,请问什么原因? @by_jack   @李先静ABSURD_AT_CSDN   @CodeBox-腾讯   @许式伟”,自然是没有得到任何人的反馈,牛人都很忙:)

于是自己就搜索google,发现了这篇文章,Linux 技巧:让进程在后台可靠运行的几种方法http://www.ibm.com/developerworks/cn/linux/l-cn-nohup/


用nohup emvdecoder&运行后,似乎运行良好了(稍后就知道这是巧合。。。),于是发一微博

“哥找到原因了,解决方案:让进程在后台可靠运行的几种方法 http://t.cn/hBEhmq”

结果晚上回家一运行,又挂了,靠,不死心,难道nohup有问题,于是用setsid,过了一天还是不行

继续测试发现其实无论用&, 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) {
	if err != nil {
		fmt.Fprintf(w,"Fatal error ", 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 := template.ParseFiles("index.html")
	checkError(w, err)

	err = t.Execute(w, emvdecoder)
	checkError(w, 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)


奇怪,发现208.94.145.109的访问,这是神吗东西,爬虫吗?

google http: request method or response status code does not allow body

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. 


go源代码: http://golang.org/src/pkg/net/http/server.go
   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,于是用
philsong@compiler:~$ curl -I 192.168.7.14:9090
curl: (52) Empty reply from server

测试,果然崩溃了,崩溃信息一致,到此bug定位了。。。

修复方案:注释掉

		//os.Exit(1)

再次运行:
philsong@compiler:~$ curl -I 192.168.7.14:9090
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

GOD,一切正常了:)小伙伴们开心的笑了。。。。

你可能感兴趣的:(golang)