diff --git a/main.go b/main.go index bc2f7d4..393802c 100644 --- a/main.go +++ b/main.go @@ -1,15 +1,8 @@ package main import ( - "crypto/sha1" - "fmt" - "io/ioutil" "log" "net/http" - "net/http/httputil" - "net/url" - "sort" - "strings" ) func main() { @@ -26,111 +19,3 @@ func main() { //CreateDefaultMenu() http.ListenAndServe(":65500", nil) } - -//apiV1Main version 1 main entry for all wechat callbacks -// -func apiV1Main(w http.ResponseWriter, r *http.Request) { - logRequestDebug(httputil.DumpRequest(r, true)) - - switch r.Method { - case "POST": - answerWechatPost(w, r) - case "GET": - answerInitialAuth(w, r) - default: - log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method)) - fmt.Fprintf(w, "Protocol Error: Expect GET or POST only") - } -} - -// -//answerInitialAuth, when wechat first verify our URL for API hooks -// -func answerInitialAuth(w http.ResponseWriter, r *http.Request) { - rq := r.URL.RawQuery - m, _ := url.ParseQuery(rq) - - echostr, eok := m["echostr"] - if checkSignature(r) && eok { - fmt.Fprintf(w, echostr[0]) - } else { - fmt.Fprintf(w, "wtf is wrong with the Internet") - } -} - -//answerWechatPost distribute PostRequest according to xml body info -// -func answerWechatPost(w http.ResponseWriter, r *http.Request) { - body, _ := ioutil.ReadAll(r.Body) - fmt.Printf("get body: %s", string(body)) - s := ReadEncryptedMsg(string(body)) - //fmt.Printf("to decrypt %s", s.Encrypt) - d := Decode(s.Encrypt) - fmt.Printf("decrypt as: %s", d) - h := ReadCommonHeader(d) - reply, _ := BuildTextMsg(h.MsgType, h.FromUserName) - fmt.Fprint(w, reply) - return -} - -// -func checkSignature(r *http.Request) bool { - rq := r.URL.RawQuery - m, _ := url.ParseQuery(rq) - - signature, sok := m["signature"] - timestamp, tok := m["timestamp"] - nonce, nok := m["nonce"] - token := APIConfig.Token - if sok && tok && nok { - //sort token, timestamp, nonce and join them - strs := []string{token, timestamp[0], nonce[0]} - sort.Strings(strs) - s := strings.Join(strs, "") - - //calculate sha1 - h := sha1.New() - h.Write([]byte(s)) - calculated := fmt.Sprintf("%x", h.Sum(nil)) - return signature[0] == calculated - } - return false -} - -func checkSignature1() bool { - s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c" - t1 := "1492970761" - n1 := "1850971833" - token := APIConfig.Token - - strs := []string{token, t1, n1} - sort.Strings(strs) - s := strings.Join(strs, "") - - h := sha1.New() - h.Write([]byte(s)) - us := fmt.Sprintf("%x", h.Sum(nil)) - return s1 == us -} - -//webrootHandler sending contents to client when request "/" -// essentially to prove the webserver is still alive -// echo query string to the client -func webrootHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:]) - rq := r.URL.RawQuery - m, _ := url.ParseQuery(rq) - for index, element := range m { - fmt.Fprintf(w, "\r\n%s => %s", index, element) - } - logRequestDebug(httputil.DumpRequest(r, true)) - -} - -func logRequestDebug(data []byte, err error) { - if err == nil { - fmt.Printf("%s\n\n", data) - } else { - log.Fatalf("%s\n\n", err) - } -} diff --git a/server.go b/server.go new file mode 100644 index 0000000..e2d1cb4 --- /dev/null +++ b/server.go @@ -0,0 +1,121 @@ +package main + +import ( + "crypto/sha1" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/httputil" + "net/url" + "sort" + "strings" +) + +//apiV1Main version 1 main entry for all wechat callbacks +// +func apiV1Main(w http.ResponseWriter, r *http.Request) { + logRequestDebug(httputil.DumpRequest(r, true)) + + switch r.Method { + case "POST": + answerWechatPost(w, r) + case "GET": + answerInitialAuth(w, r) + default: + log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method)) + fmt.Fprintf(w, "Protocol Error: Expect GET or POST only") + } +} + +// +//answerInitialAuth, when wechat first verify our URL for API hooks +// +func answerInitialAuth(w http.ResponseWriter, r *http.Request) { + rq := r.URL.RawQuery + m, _ := url.ParseQuery(rq) + + echostr, eok := m["echostr"] + if checkSignature(r) && eok { + fmt.Fprintf(w, echostr[0]) + } else { + fmt.Fprintf(w, "wtf is wrong with the Internet") + } +} + +//answerWechatPost distribute PostRequest according to xml body info +// +func answerWechatPost(w http.ResponseWriter, r *http.Request) { + body, _ := ioutil.ReadAll(r.Body) + fmt.Printf("get body: %s", string(body)) + s := ReadEncryptedMsg(string(body)) + //fmt.Printf("to decrypt %s", s.Encrypt) + d := Decode(s.Encrypt) + fmt.Printf("decrypt as: %s", d) + h := ReadCommonHeader(d) + reply, _ := BuildTextMsg(h.MsgType, h.FromUserName) + fmt.Fprint(w, reply) + return +} + +// +func checkSignature(r *http.Request) bool { + rq := r.URL.RawQuery + m, _ := url.ParseQuery(rq) + + signature, sok := m["signature"] + timestamp, tok := m["timestamp"] + nonce, nok := m["nonce"] + token := APIConfig.Token + if sok && tok && nok { + //sort token, timestamp, nonce and join them + strs := []string{token, timestamp[0], nonce[0]} + sort.Strings(strs) + s := strings.Join(strs, "") + + //calculate sha1 + h := sha1.New() + h.Write([]byte(s)) + calculated := fmt.Sprintf("%x", h.Sum(nil)) + return signature[0] == calculated + } + return false +} + +func checkSignature1() bool { + s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c" + t1 := "1492970761" + n1 := "1850971833" + token := APIConfig.Token + + strs := []string{token, t1, n1} + sort.Strings(strs) + s := strings.Join(strs, "") + + h := sha1.New() + h.Write([]byte(s)) + us := fmt.Sprintf("%x", h.Sum(nil)) + return s1 == us +} + +//webrootHandler sending contents to client when request "/" +// essentially to prove the webserver is still alive +// echo query string to the client +func webrootHandler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:]) + rq := r.URL.RawQuery + m, _ := url.ParseQuery(rq) + for index, element := range m { + fmt.Fprintf(w, "\r\n%s => %s", index, element) + } + logRequestDebug(httputil.DumpRequest(r, true)) + +} + +func logRequestDebug(data []byte, err error) { + if err == nil { + fmt.Printf("%s\n\n", data) + } else { + log.Fatalf("%s\n\n", err) + } +} diff --git a/server_test.go b/server_test.go new file mode 100644 index 0000000..6ae1f60 --- /dev/null +++ b/server_test.go @@ -0,0 +1,42 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +//when we setup wechate parameters,we chat will verify us +func TestInitialSetup(t *testing.T) { + SetupConfig() +} + +func TestWebRootHandler(t *testing.T) { + // Create a request to pass to our handler. We don't have any query parameters for now, so we'll + // pass 'nil' as the third parameter. + req, err := http.NewRequest("GET", "/dummydir", nil) + if err != nil { + t.Fatal(err) + } + + // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response. + rr := httptest.NewRecorder() + handler := http.HandlerFunc(webrootHandler) + + // Our handlers satisfy http.Handler, so we can call their ServeHTTP method + // directly and pass in our Request and ResponseRecorder. + handler.ServeHTTP(rr, req) + + // Check the status code is what we expect. + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + // Check the response body is what we expect. + expected := `Hi there, I love dummydir!` + if rr.Body.String() != expected { + t.Errorf("handler returned unexpected body: got %v want %v", + rr.Body.String(), expected) + } +}