| package main | package main | ||||
| import ( | import ( | ||||
| "crypto/sha1" | |||||
| "fmt" | |||||
| "io/ioutil" | |||||
| "log" | "log" | ||||
| "net/http" | "net/http" | ||||
| "net/http/httputil" | |||||
| "net/url" | |||||
| "sort" | |||||
| "strings" | |||||
| ) | ) | ||||
| func main() { | func main() { | ||||
| //CreateDefaultMenu() | //CreateDefaultMenu() | ||||
| http.ListenAndServe(":65500", nil) | 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) | |||||
| } | |||||
| } |
| 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) | |||||
| } | |||||
| } |
| 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) | |||||
| } | |||||
| } |