基于 Gin 的 HTTP 代理上网行为记录 demo

前言: 前端时间写了好几篇使用 Gin 框架来做 HTTP 代理 demo 的文章,然后就想着做一个记录上网行为的小工具,就是简单记录看看平时访问了什么网站(基于隧道代理的,不是中间人代理,所以只能记录去了哪里,不能记录干了什么)。不过因为编译问题一直没有解决,我又不想重新在 Windows 上安装 Golang 的开发环境,所以就把它搁置了。最近正好把那个交叉编译的问题解决了,所以就把这个博客也发出来吧。

一、代码

主要的代码还是前几篇博客已经介绍过的了,我只是加了一个写入 sqlite3 的功能。因为也没有必要有一条一条的写入,所以就是每 100 条写入一次了。程序运行会在当前目录生成一个 net_access.db 的文件,这个就是 sqlite 的数据库文件。这里只有一个记录访问了什么网站的表,本来还想着做一个 block_list 的表,阻止访问某些网站的(就是不给它建立连接),不过放弃了。

package mainimport ("bufio""io""log""net""net/http""strings""time""github.com/gin-gonic/gin""gorm.io/driver/sqlite""gorm.io/gorm"
)type ReqRecord struct {ID        uint      `gorm:"primaryKey,column:id"`CreatedAt time.Time `gorm:"column:time"`Schema    string    `gorm:"column:schema"` // http/httpsHost      string    `gorm:"column:host"`Port      string    `gorm:"column:port"`
}const (RPOXY_SERVER  = "CrazyDragonHttpProxy"                                                             // it is just a kidding, but Only HTTP!TUNNEL_PACKET = "HTTP/1.1 200 Connection Established\r\nProxy-agent: CrazyDragonHttpProxy\r\n\r\n" // Don'e USE `` to surround a protocl strng, DAMN!!!BUFFER_SIZE   = 100
)var (proxyHttpClient = http.DefaultClientDBConn          *gorm.DBrecordChan      chan *ReqRecord
)func init() {var err errorDBConn, err = gorm.Open(sqlite.Open("net_access.db"), &gorm.Config{})if err != nil {panic("failed to connect database")}err = DBConn.AutoMigrate(&ReqRecord{})if err != nil {panic(err)}recordChan = make(chan *ReqRecord, BUFFER_SIZE) // 缓冲区大小为 100
}func main() {go write2DB()r := gin.Default()r.NoRoute(routeProxy)   // NO Route is every Route!!!r.Run("127.0.0.1:8888")
}// Then I can process all routes
func routeProxy(c *gin.Context) {req := c.Requestgo recordReq(req) // 记录日志if req.Method == http.MethodConnect {httpsProxy(c, req) // create http tunnel to process https} else {httpProxy(c, req) // process plain http}
}func httpsProxy(c *gin.Context, req *http.Request) {// established connect tunneladdress := req.URL.Host // it contains the porttunnelConn, err := net.DialTimeout("tcp", address, 10*time.Second)if err != nil {log.Println(err)return}log.Printf("try to established Connect Tunnel to: %s has been successfully.\n", address)tunnelrw := bufio.NewReadWriter(bufio.NewReader(tunnelConn), bufio.NewWriter(tunnelConn))c.Status(200) // 不加也没有问题,只是说 https 响应都会变成 404.// And We need to take over the http connection, Then make it become a TCP connection.hj, ok := c.Writer.(http.Hijacker)if !ok {http.Error(c.Writer, "webserver doesn't support hijacking", http.StatusInternalServerError)return}clientConn, bufrw, err := hj.Hijack()if err != nil {http.Error(c.Writer, err.Error(), http.StatusInternalServerError)return}// 建立隧道之后,发送一个连接建立的响应(其实,只要状态码是 200 就可以了)if _, err = clientConn.Write([]byte(TUNNEL_PACKET)); err != nil {log.Printf("Response Failed: %v", err.Error())} else {log.Println("Response Success.")}// data flow direction: client <---> tunnel <---> serverdefer clientConn.Close()defer tunnelConn.Close()done := make(chan struct{})go transfer(bufrw, tunnelrw, done) // client --> proxy --> servergo transfer(tunnelrw, bufrw, done) //server --> proxy --> client<-done
}func httpProxy(c *gin.Context, req *http.Request) {req.RequestURI = "" // Must create a new Req or empty this.resp, err := proxyHttpClient.Do(req)if err != nil {log.Println(err)return}defer resp.Body.Close()c.Status(resp.StatusCode) // change the status code, default is 404 !!!for k, v := range resp.Header {c.Header(k, strings.Join(v, ",")) // write Header}c.Header("Server", RPOXY_SERVER) // haha, it just a kidding!!!io.Copy(c.Writer, resp.Body)     // and response data to client
}// tunnel transfer data.
func transfer(from io.Reader, to io.Writer, ch chan<- struct{}) {io.Copy(to, from)ch <- struct{}{}
}// Record http request
func recordReq(req *http.Request) {var schema = "HTTPS"if req.Method != http.MethodConnect {schema = "HTTP"}record := &ReqRecord{Schema: schema,Host:   req.Host,Port:   req.URL.Port(),}recordChan <- recordlog.Printf("Method: %s, Host: %s, URL: %s, Version: %s\n", req.Method, req.Host, req.URL.Path, req.Proto)
}func write2DB() {log.Printf("Start to record request info...")records := make([]*ReqRecord, BUFFER_SIZE)idx := 0for {r := <-recordChanrecords[idx] = ridx++if idx == BUFFER_SIZE {// 批量写入DBConn.Create(&records)idx = 0 // reset positionlog.Printf("Write to DB successfully.")}}
}

二、演示

启动程序

在这里插入图片描述

配置系统代理

注:这里没有配置绕过环回地址这些,最后的方式是先启动 fiddler,然后把它的配置复制下来改一下就可以了。

在这里插入图片描述
查看 db 文件

昨天晚上到现在,记录了 2800 条数据了。因为这个代理的方式实现的类似早期的 HTTP 协议了,没有持久连接,可能是这个原因导致的请求更多了。

在这里插入图片描述

然后聚合查询看看访问最多的网站是什么,没想到居然是 dockerdesktop,这玩意开着就一直进行网络访问呀,不知道它干了什么!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/226015.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

wps左上角有绿色小三角的数字如何求和

1.这个状态是求和不了的&#xff0c;使用求和公式求出来的也是0 2.进行如下操作 3.转换好后 则可以求和成功了

深度学习 Day16——P5运动鞋识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 文章目录 前言1 我的环境2 代码实现与执行结果2.1 前期准备2.1.1 引入库2.1.2 设置GPU&#xff08;如果设备上支持GPU就使用GPU,否则使用C…

【Qt QML 入门】TextArea

TextArea也是一个多行文本编辑器。TextArea相比texttedit&#xff0c;增加了占位符文本&#xff0c;并添加了样式定义。 import QtQuick import QtQuick.Window import QtQuick.ControlsWindow {id: winwidth: 800height: 600visible: trueTextArea {id: taanchors.centerIn: …

解决Visual Studio 各版本都出现新建项目后解决方案下没有文件和项目问题

一步一步创建C#控制台应用程序也会出错&#xff0c;这个你可能不会相信&#xff0c;我就遇到了这么一次&#xff0c;就在刚刚&#xff0c;是的&#xff0c;我都不敢相信&#xff0c;用了这么多年的新建一个控制台程序居然不正常了。新建完毕发现里面什么都没有&#xff0c;除了…

首发卡密引流系统源码

程序特色&#xff1a; 支持个人和企业小程序广告获取卡密。 支持短视频点赞和关注获取卡密。 搭建教程&#xff1a; 环境要求&#xff1a;Nginx、MySQL 5.6、PHP 5.6 步骤&#xff1a; 将压缩包解压至网站根目录。 打开域名/install&#xff0c;按照提示填写数据库信息进行…

JS对象循环引用的危害:你知道吗?

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

移动端机器学习框架 MDL 简介与实践

Mobile-deep-learning&#xff08;MDL&#xff09; MDL 是百度研发的可以部署在移动端的基于卷积神经网络实现的移动端框架&#xff0c;可以应用在图像识别领域。 具体应用&#xff1a;在手机百度 App 中&#xff0c;用户只需要点击自动拍开关&#xff0c;将手机对准物体&…

联想王传东:AI PC迈入AI Ready 即将开启AI On

“AI PC已经正式迈入AI Ready 阶段&#xff0c;接下来会逐渐进入到AI On阶段。”12月16日&#xff0c;英特尔人工智能创新应用大赛启动仪式在深圳举办。作为独家AI PC合作伙伴&#xff0c;联想集团副总裁、中国区首席市场官王传东代表公司出席仪式并致辞。 王传东认为AI PC的发…

大模型Transformer 推理 :kvCache原理浅析

大模型Transformer 推理 :kvCache原理浅析 kvCache 原理 在采样时,Transformer模型会以给定的提示/上下文作为初始输入进行推理(可以并行处理),然后逐一生成额外的标记来继续完善生成的序列(体现了模型的自回归性质)。在采样过程中,Transformer会执行自注意力操作,为…

27系列DGUS智能屏发布:可实时播放高清模拟信号摄像头视频

针对高清晰度的模拟信号摄像头视频画面的显示需求&#xff0c;迪文特推出27系列DGUS智能屏。该系列智能屏可适配常见的AHD摄像头、CVBS摄像头&#xff0c;支持单路1080P高清显示、两路720P同屏显示&#xff08;同一类型摄像头&#xff09;。用户通过DGUS简单开发即可实现摄像头…

【送书活动】智能汽车、自动驾驶、车联网的发展趋势和关键技术

文章目录 前言01 《智能汽车》推荐语 02 《SoC底层软件低功耗系统设计与实现》推荐语 03 《SoC设计指南》推荐语 05 《智能汽车网络安全权威指南&#xff08;上册&#xff09;》推荐语 06 《智能汽车网络安全权威指南&#xff08;下册&#xff09;》推荐语 后记赠书活动 前言 …

mac安装pnpm与使用

1、什么是pnpm&#xff1f; pnpm 全称 performant npm&#xff0c;意思是高性能的 npm。pnpm 由 npm/yarn 衍生而来&#xff0c;解决了 npm/yarn 内部潜在的 bug&#xff0c;极大的优化了性能&#xff0c;扩展了使用场景。被誉为 “最先进的包管理工具”。 2、pnpm特点 速度…

虚拟机启动 I/O error in “xfs_read_agi+0x95“

1.在选择系统界面按e 进入维护模式 2.找到ro把ro改成 rw init/sysroot/bin/sh 然后按Ctrlx 3.找到坏掉的分区&#xff0c;以nvme0n1p3为例进行修复 xfs_repair -d /dev/nvme0n1p3 4.init 6 重新启动 以下情况 先umount 再修复 则修复成功

接口测试 — 4.Requests库GET、Post请求

Requests库GET请求是使用HTTP协议中的GET请求方式对目标网站发起请求。 &#xff08;不带参数的GET请求请看上一篇文章的练习&#xff09; 1、Requests库待参数的GET请求 使用Get方法带参数请求时&#xff0c;是params参数字典&#xff0c;而不是data参数字典。data参数字典…

dcoker-compose一键部署EFAK —— 筑梦之路

简介 EFAK&#xff08;Eagle For Apache Kafka&#xff0c;以前称为 Kafka Eagle&#xff09;是一款由国内公司开源的Kafka集群监控系统&#xff0c;可以用来监视kafka集群的broker状态、Topic信息、IO、内存、consumer线程、偏移量等信息&#xff0c;并进行可视化图表展示。独…

Arduino中以太网Udp通信

目录 1、测试硬件 2、程序 &#xff08;0&#xff09;头文件添加 &#xff08;1&#xff09;变量定义 &#xff08;2&#xff09;初始化程序 &#xff08;3&#xff09;循环执行程序 3、程序下载 &#xff08;1&#xff09;开发板控制器和端口号选择 &#xff08;2&am…

pycharm某个xxx.sh文件显示问号,无法编辑

文章目录 pycharm某个xxx.sh文件显示问号,无法编辑其他参考 pycharm某个xxx.sh文件显示问号,无法编辑 问题描述&#xff1a;pycharm某个xxx.sh文件显示问号,无法编辑 问题分析&#xff1a; pycharm无法识别文件类型。 问题解决&#xff1a; 在pycharm中选中该文件&#xff0…

BugKu-Web-滑稽

题目环境 持续的动态图片 F12审查元素 拿下flag&#xff1a;flag{595d994a34342417bfc3a3c3a23e0a48}

synchronized关键字的使用和原理

synchronized关键字的使用和原理 synchronized&#xff1a;对象锁&#xff0c;保证了临界区内代码的原子性&#xff0c;采用互斥的方式让同一时刻至多只有一个线程能持有对象锁&#xff0c;其它线程获取这个对象锁时会阻塞&#xff0c;保证拥有锁的线程可以安全的执行临界区内…

【Android】MVC与MVP的区别,MVP网络请求实践

一、MVC模式 目录 一、MVC模式二、MVP模式 1、MVP的简单应用 1.1 导入相关依赖包并设置权限1.2 实现Model1.2 实现Presenter1.3 实现View1.4分析项目结构和绑定过程1.5效果展示 2、MVP结合RxJava 一、MVC模式 MVC&#xff08;Model(模型)——View(视图)——Controller(控制…