V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
ser3w
V2EX  ›  问与答

寻求一个可以抓到返回 body 并同步写入 redis 的 http 请求转发工具(openresty 只能非阻塞写入)

  •  1
     
  •   ser3w · 6 天前 · 282 次点击

    背景: 需要将会话存档接口/cgi-bin/gettoken 抓取返回的 body 并写入到 redis(需要阻塞进行)

    问题:在代理/cgi-bin/gettoken 等接口时 go 语言完全没问题,但是有一个/cgi-bin/message/getchatmediadata 接口会报解密错误,因为企微会话存档 sdk 为黑盒,但使用 openresty 可以成功,暂时不知道怎么解决

    自己觉得有如下可能:

    1.断点续传实现有问题,但是无法测试 2.是不是请求中有没有转发过去的部分

    apisix 这种太重了,尽量可以使用 go 或者 openresty+lua 实现 抓到的请求:

    "客户端请求内容: &{Method:POST URL:/cgi-bin/message/getchatmediadata?access_token=xxx Proto:HTTP/1.0 ProtoMajor:1 ProtoMinor:0 Header:map[Accept:[*/*] Content-Length:[658] Content-Type:[application/x-www-form-urlencoded] Range:[bytes=0-524287]] Body:0xc000464e80 GetBody:<nil> ContentLength:658 TransferEncoding:[] Close:true Host:xxxxxx Form:map[] PostForm:map[] MultipartForm:<nil> Trailer:map[] RemoteAddr:192.168.16.56:44636 RequestURI:/cgi-bin/message/getchatmediadata?access_token=xxx TLS:<nil> Cancel:<nil> Response:<nil> ctx:0xc0004468c0 pat:<nil> matches:[] otherValues:map[]}"}
    

    go 代码示例如下

    package main
    
    import (
    	"fmt"
    	"io"
    	"log"
    	"net/http"
    	"net/url"
    	"strconv"
    )
    
    func main() {
    	// 设置上游服务器的地址
    	upstreamURL := "https://qyapi.weixin.qq.com" // 替换为您的上游服务器地址
    
    	// 创建一个处理请求的函数
    	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    		// 构建目标 URL ,将请求的路径和查询参数添加到上游 URL
    		proxyURL, err := url.Parse(upstreamURL)
    		if err != nil {
    			http.Error(w, "无效的上游服务器地址", http.StatusInternalServerError)
    			return
    		}
    		proxyURL.Path += r.URL.Path
    		proxyURL.RawQuery = r.URL.RawQuery // 传递查询参数
    
    		// 创建一个新的请求
    		req, err := http.NewRequest(r.Method, proxyURL.String(), r.Body)
    		if err != nil {
    			http.Error(w, "无法创建请求", http.StatusInternalServerError)
    			return
    		}
    
    		// 复制请求头
    		req.Header = r.Header
    
    		// 发送请求到上游服务器
    		client := &http.Client{}
    		resp, err := client.Do(req)
    		if err != nil {
    			http.Error(w, "上游请求失败", http.StatusBadGateway)
    			return
    		}
    		defer resp.Body.Close()
    
    		// 处理 Range 请求
    		if rangeHeader := r.Header.Get("Range"); rangeHeader != "" {
    			// 解析 Range 请求
    			var start, end int64
    			_, err := fmt.Sscanf(rangeHeader, "bytes=%d-%d", &start, &end)
    			if err != nil {
    				http.Error(w, "无效的 Range 请求", http.StatusRequestedRangeNotSatisfiable)
    				return
    			}
    
    			// 计算文件大小(假设上游服务器返回了 Content-Length )
    			contentLength := resp.ContentLength
    			if end >= contentLength {
    				end = contentLength - 1
    			}
    
    			// 设置响应头
    			w.Header().Set("Content-Type", resp.Header.Get("Content-Type"))
    			w.Header().Set("Content-Length", strconv.FormatInt(end-start+1, 10))
    			w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, contentLength))
    			w.WriteHeader( http.StatusPartialContent)
    
    			// 读取并写入指定范围的响应体
    			buf := make([]byte, end-start+1)
    			_, err = resp.Body.Read(buf)
    			if err != nil && err != io.EOF {
    				http.Error(w, "读取响应失败", http.StatusInternalServerError)
    				return
    			}
    			w.Write(buf)
    			return
    		}
    
    		// 设置响应状态码和响应头
    		w.WriteHeader(resp.StatusCode)
    		for key, value := range resp.Header {
    			w.Header()[key] = value
    		}
    
    		// 写入响应体
    		io.Copy(w, resp.Body)
    		log.Println(r.Method, r.RequestURI, '\n')
    		//fmt.Println(r.Method, r.RequestURI)
    	})
    
    	// 启动服务器
    	log.Println("启动反向代理服务器,监听 :8080")
    	log.Fatal( http.ListenAndServe(":8080", nil))
    }
    
    
    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5659 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 03:25 · PVG 11:25 · LAX 20:25 · JFK 23:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.