需求:软件公司的软件经常卡或者直接死掉,他们居然还没有日志,看服务器的情况CPU和内存还有网路一直平稳,但是会突然一下CPU或者内存,网络其中有一项会暴涨,想知道什么原因,所以弄一个http代理
代码:
package main
import (
"bytes"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
// NewProxy 拿到 targetHost 后,创建一个反向代理
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, err := url.Parse(targetHost)
if err != nil {
return nil, err
}
return httputil.NewSingleHostReverseProxy(url), nil
}
// ProxyRequestHandler 使用 proxy 处理请求
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if strings.Contains(r.URL.RequestURI(), "adpweb/a") {
// 获取请求参数
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = ioutil.ReadAll(r.Body)
}
// 一定要回写回去,因为上面已经读取清空了
r.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))
log.Println("请求参数", string(bodyBytes))
// log.Println("URL:", r.URL.RequestURI())
// log.Println("请求Body报文长度:", r.ContentLength)
// log.Println("响应报文长度:", w.Header()["Content-Length"])
// 写入响应数据,会出现在最前面
// w.Write([]byte("xxxx"))
}
proxy.ServeHTTP(w, r)
}
}
type transport struct {
http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
// 一定要回写回去,因为上面已经读取清空了
body := ioutil.NopCloser(bytes.NewReader(b))
resp.Body = body
if strings.Contains(req.URL.RequestURI(), "adpweb/a") {
log.Println("响应Body报文长度:", len(b))
// log.Println("响应数据:", string(b))
log.Println("URL:", req.URL.RequestURI())
}
return resp, nil
}
func main() {
log.Println("转发程序开始!!!")
proxy, err := NewProxy("http://192.168.0.23:8083")
if err != nil {
panic(err)
}
// 获取响应数据
proxy.Transport = &transport{http.DefaultTransport}
// 使用 proxy 处理所有请求到你的服务
http.HandleFunc("/", ProxyRequestHandler(proxy))
log.Fatal(http.ListenAndServe(":7654", nil))
}
需求2:因为软件公司有一个安卓程序返回的错误信息很笼统也模糊,更没有给出应该怎么做的方案,导致使用者根本不知道应该怎么处理,只能去ERP上各种的查数据才知道怎么回事儿,我自己给他们写了一个安卓程序,用于发现问题以后可以用我的安卓程序处理,但是他们懒得用,正好软件公司的安卓程序设置中可以修改IP端口来区分测试和正式,于是我想着直接用代理的方式获取所有请求,然后对返回错误信息的请求做处理
思路:在上面代码的基础上加上相应修改
代码:
package main
import (
"bytes"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
// 单据前缀列表
var danhaoList = []string{"5101", "5102", "5103", "5104", "5109", "5199", "5301"}
// 需要的请求参数
var qiongqiucanshu = ""
// 请求URL
var qingqiuURL = ""
// 需要处理的URL列表
var URLList = []string{"/adpweb/api/wms/boxSave", "/adpweb/api/wms/scanBarAndAll"}
// NewProxy 拿到 targetHost 后,创建一个反向代理
func NewProxy(targetHost string) (*httputil.ReverseProxy, error) {
url, _ := url.Parse(targetHost)
return httputil.NewSingleHostReverseProxy(url), nil
}
// ProxyRequestHandler 使用 proxy 处理请求
func ProxyRequestHandler(proxy *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
// 获取请求参数
var bodyBytes []byte
if r.Body != nil {
bodyBytes, _ = ioutil.ReadAll(r.Body)
}
// 获取参数
enEscapeUrl, _ := url.QueryUnescape(string(bodyBytes))
if strings.Contains(enEscapeUrl, "barcode=") {
// 扫码
barcode := strings.Split(strings.Split(enEscapeUrl, "barcode=")[1], "&")[0]
if strings.Contains(barcode, "#") {
// 赋值参数和URL
qiongqiucanshu = barcode
qingqiuURL = r.RequestURI
}
}
// 一定要回写回去,因为上面已经读取清空了
r.Body = ioutil.NopCloser(bytes.NewReader(bodyBytes))
// 修改响应
proxy.ModifyResponse = modifyResponse
// 修改响应头,不然显示的不是json,Header().Set()必须在WriteHeader()上面,不然不会生效
w.Header().Set("Content-Type", "application/json")
// 修改状态,不写的话会报错
w.WriteHeader(200)
proxy.ServeHTTP(w, r)
}
}
// 响应json的结构体
type Data struct {
ErrorCode string `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
Status int `json:"status"`
}
// 响应的结构体
type MainData struct {
Head *Data `json:"head"`
}
// 爬虫结果的结构体
type PCData struct {
Code int `json:"code"`
Msg string `json:"msg"`
}
// 修改响应
func modifyResponse(resp *http.Response) error {
// 获取响应
oldPayload, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
}
if resp.StatusCode == 200 {
// 响应结构体
var maindata MainData
var allstudentjson = oldPayload
// 将json字符串转为结构体
if err := json.Unmarshal([]byte(string(oldPayload)), &maindata); err == nil {
inUrl := in(qingqiuURL, URLList)
if qiongqiucanshu != "" && inUrl {
log.Println("请求URL:", qingqiuURL)
log.Println("扫描条码:", qiongqiucanshu)
// 根据响应数据做处理
if maindata.Head.ErrorCode == "0000" {
var wentitype = "0"
if strings.Contains(maindata.Head.ErrorMsg, "小于扫描量") {
wentitype = "1"
}
if strings.Contains(maindata.Head.ErrorMsg, "单箱级") {
wentitype = "2"
}
if strings.Contains(maindata.Head.ErrorMsg, "料件不存在") {
wentitype = "3"
}
if wentitype != "0" {
qianzhui := strings.Split(qiongqiucanshu, "-")[0]
result := in(qianzhui, danhaoList)
formdatas := make(map[string]string)
if result {
formdatas["wentitype"] = wentitype
formdatas["tiaomatype"] = "1"
formdatas["danhao"] = strings.Split(qiongqiucanshu, "#")[0]
formdatas["picihao"] = strings.Split(qiongqiucanshu, "#")[1]
} else {
formdatas["wentitype"] = wentitype
formdatas["tiaomatype"] = "2"
formdatas["pinhao"] = strings.Split(qiongqiucanshu, "#")[0]
formdatas["picihao"] = strings.Split(qiongqiucanshu, "#")[1]
}
bytesData, _ := json.Marshal(formdatas)
res, err := http.Post("http://192.168.0.92:8080/renren-fast/xinxi/xinxiwentifangan/pdawenti",
"application/json;charset=utf-8", bytes.NewBuffer([]byte(bytesData)))
if err != nil {
log.Println("Fatal error ", err.Error())
}
defer res.Body.Close()
content, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Println("Fatal error ", err.Error())
}
log.Println("爬虫数据::", string(content))
var padata PCData
var pcneirong = ""
if err := json.Unmarshal([]byte(string(content)), &padata); err == nil {
pcneirong = padata.Msg
} else {
log.Println(err)
}
// 修改相应内容
maindata.Head.ErrorMsg = pcneirong + "\n" + maindata.Head.ErrorMsg
// 结构体转json字符串
allstudentjson, _ = json.Marshal(maindata)
}
}
}
oldPayload = allstudentjson
} else {
log.Println(err)
}
}
// 修改的内容回写
resp.Body = ioutil.NopCloser(bytes.NewBuffer(oldPayload))
return nil
}
// 字符串是否存在数组中
func in(target string, str_array []string) bool {
for _, element := range str_array {
if target == element {
return true
}
}
return false
}
func main() {
log.Println("转发程序开始!!!")
var dailiip = "http://192.168.0.23:8080"
var prok = ":7655"
log.Println("当前代理服务器:", dailiip)
log.Println("当前监控的端口:", prok)
// initialize a reverse proxy and pass the actual backend server url here
// 初始化反向代理并传入真正后端服务的地址
proxy, err := NewProxy(dailiip)
if err != nil {
log.Println(err)
}
// 使用 proxy 处理所有请求到你的服务
http.HandleFunc("/", ProxyRequestHandler(proxy))
log.Fatal(http.ListenAndServe(prok, nil))
}
问题:上面的程序会报一个错误
http: superfluous response.WriteHeader call from net/http/httputil.(*ReverseProxy).ServeHTTP (reverseproxy.go:507)
我查了一下说是重复调用WriteHeader()方法,但是我并没有找到其它地方的调用,知道的朋友可以告诉我一下原因,谢谢