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

news2024/9/22 4:10:51

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

一、代码

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

package main

import (
	"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/https
	Host      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.DefaultClient
	DBConn          *gorm.DB
	recordChan      chan *ReqRecord
)

func init() {
	var err error
	DBConn, 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.Request
	go 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 tunnel
	address := req.URL.Host // it contains the port
	tunnelConn, 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 <---> server
	defer clientConn.Close()
	defer tunnelConn.Close()
	done := make(chan struct{})
	go transfer(bufrw, tunnelrw, done) // client --> proxy --> server
	go 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 <- record
	log.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 := 0
	for {
		r := <-recordChan
		records[idx] = r
		idx++
		if idx == BUFFER_SIZE {
			// 批量写入
			DBConn.Create(&records)
			idx = 0 // reset position
			log.Printf("Write to DB successfully.")
		}
	}
}

二、演示

启动程序

在这里插入图片描述

配置系统代理

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

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

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

在这里插入图片描述

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

在这里插入图片描述

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

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

相关文章

智能优化算法应用:基于JAYA算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于JAYA算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于JAYA算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.JAYA算法4.实验参数设定5.算法结果6.参考文献7.MA…

算法设计基础——综合

算法设计基础中最基础的几种算法&#xff1a;分治法、减治法、贪心法、动态规划法、回溯法基本都掌握后&#xff0c;我们现在可以对这些算法做整体的比较&#xff0c;本次实验使用蛮力法、动态规划法、回溯法来求解0/1背包问题&#xff0c;来比较各个算法的优劣。 1. 蛮力法 …

Lua【通俗易懂】

目录 1.什么是lua❤️❤️❤️ 2.特点 ❤️❤️❤️ 3.安装 ❤️❤️❤️ 4.Lua使用❤️❤️❤️ 5.Lua的注释 ❤️❤️❤️ 6.数据类型 ❤️❤️❤️ 7.控制结构 1.什么是lua❤️❤️❤️ Lua是一种轻量、小巧的脚本语言,用标准C语言编写并以源代码形式开发。设计的目…

Python基础08-文件操作详解

零、文章目录 Python基础08-文件操作详解 1、文件操作概述 &#xff08;1&#xff09;文件是什么 内存中存放的数据在计算机关机后就会消失。要长久保存数据&#xff0c;就要使用硬盘、光盘、U 盘等设备。为了便于数据的管理和检索&#xff0c;引入了**“文件”**的概念。 …

论文阅读——Semantic-SAM

Semantic-SAM可以做什么&#xff1a; 整合了七个数据集&#xff1a; 一般的分割数据集&#xff0c;目标级别分割数据集&#xff1a;MSCOCO, Objects365, ADE20k 部分分割数据集&#xff1a;PASCAL Part, PACO, PartImagenet, and SA-1B The datasets are SA-1B, COCO panopt…

云原生之深入解析亿级流量架构之服务限流思路与方法

一、限流思路 ① 熔断 系统在设计之初就把熔断措施考虑进去&#xff0c;当系统出现问题时&#xff0c;如果短时间内无法修复&#xff0c;系统要自动做出判断&#xff0c;开启熔断开关&#xff0c;拒绝流量访问&#xff0c;避免大流量对后端的过载请求。系统也应该能够动态监测…

工业性能CCD图像处理+

目录 硬件部分 ​编辑 软件部分 CCD新相机的调试处理&#xff08;更换相机处理&#xff0c;都要点执行检测来查看图像变化&#xff09; 问题:新相机拍摄出现黑屏&#xff0c;图像拍摄不清晰&#xff0c;&#xff08;可以点击图像&#xff0c;向下转动鼠标的滚轮&#xff08…

uniGUI学习之UniHTMLMemo1富文本编辑器

1]系统自带的富文本编辑器 2]jQueryBootstarp富文本编辑器插件summernote.js 1]系统自带的富文本编辑器 1、末尾增加<p> 2、增加字体 3、解决滚屏问题 4、输入长度限制问题 5、显示 并 编辑 HTML源代码(主要是图片处理) 1、末尾增加<p> UniHTMLMemo1.Lines…

Ubuntu 常用命令之 ls 命令用法介绍

Ubuntu ls 命令用法介绍 ls是Linux系统下的一个基本命令&#xff0c;用于列出目录中的文件和子目录。它有许多选项可以用来改变列出的内容和格式。 以下是一些基本的ls命令选项 -l&#xff1a;以长格式列出文件&#xff0c;包括文件类型、权限、链接数、所有者、组、大小、最…

【程序】STM32 读取光栅_编码器_光栅传感器_7针OLED

文章目录 源代码工程编码器基础程序参考资料 源代码工程 源代码工程打开获取&#xff1a; http://dt2.8tupian.net/2/28880a55b6666.pg3这里做了四倍细分&#xff0c;在屏幕上显示 速度、路程、方向。 接线方法&#xff1a; 单片机--------------串口模块 单片机的5V-------…

渗透实验基础教程(完整版):

#江南的江 #每日鸡汤&#xff1a;影响我们人生的绝不仅仅是环境&#xff0c;其实是心态在控制个人的行动和思想。同时&#xff0c;心态也决定了一个人的视野事业和成就&#xff0c;甚至一生。 #初心和目标&#xff1a;成为网络安全达人。。。 渗透实验基础教程&#xff08;完整…

STM32-HAL库11-SPI通讯(F103C6T6做主机,F103C8T6做从机)

STM32-HAL库11-SPI通讯&#xff08;F103C6T6做主机&#xff0c;F103C8T6做从机&#xff09; 一、所用材料 STM32F103C6T6最小系统板-主机 STM32F103C8T6最小系统板-从机 串口调试助手X-COM 二、所学内容 主要为实现SPI的轮询发送功能&#xff0c;在DSP280049C初学&#xff…

行政前台快递管理教程

行政前台快递管理&#xff0c;成了企业前台工作人员不可逃避的话题。看似简单的几个字&#xff0c;却难倒了不少人。身边有从事行政前台工作的朋友&#xff0c;应该或多或少都会听其抱怨过...... 为什么公司寄件管理这么难&#xff1f; 为什么员工不能按规定寄件&#xff1f;…

【算法刷题】每日打卡——动态规划(1)

背包问题 例题一 有 N件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i件物品的体积是 vi&#xff0c;价值是 wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。 输入格式 第一行两个整数…

nlp与cv的发展

Transformer的出现,促进了更高容量模型的建立,为大模型的出现奠定基础. &#x1f9d0;大模型通常具有十亿个以上参数(仅供参考) &#x1f62e;左边的蓝色是CV领域、右下绿色是NLP、右上蓝色是多模态&#x1f603;基础模型(Foundational Models)首次由Bommasani等人在《Stanford…

Quartus 18.1软件及支持包安装教程

安装前最好关闭电脑的杀毒软件和防火墙 安装包可以到Quartus官网下载需要的版本&#xff0c;注意选择操作系统 Quartus官网&#xff1a;FPGA 设计软件 - 英特尔 Quartus Prime (intel.cn) 下载解压后以管理员的身份运行 QuartusSetup-18.1.0.625.exe文件&#xff0c;版本不同…

openGauss学习笔记-162 openGauss 数据库运维-备份与恢复-导入数据-通过INSERT语句直接写入数据

文章目录 openGauss学习笔记-162 openGauss 数据库运维-备份与恢复-导入数据-通过INSERT语句直接写入数据162.1 使用openGauss数据库提供的客户端工具向openGauss数据库写入数据162.2 通过JDBC/ODBC驱动连接数据库执行INSERT语句向openGauss数据库写入数据162.2.1 函数原型162.…

【JavaWeb学习笔记】10 - 手写Tomcat底层,Maven的初步使用

一、Maven 1.Maven示意图 类似Java访问数据库 2.创建Maven案例演示 配置阿里镜像 找到setting目录 但一开始配置不存在该文件 需要去Maven主目录下的conf拿到settings拷贝到上述目录 拷贝到admin/.m2后打开该settings 在<mirrors>内输入镜像地址 <mirror> …

根文件系统(一):基础

本文主要探讨210根文件系统相关知识。 根文件系统 存储设备(flash等)是分块(扇区),访问存储设备是按块号 (扇区号)来访问,文件系统(软件)对存储设备扇区进行管理,将对扇区的访问变成对目录和文件名的访问 根文件系统init进程的应用程序和其他应用程序,提供根目…

【docker】部署minio对象存储并用rclone同步

docker部署minio对象存储并用rclone同步 本文首发于 ❄️慕雪的寒舍 1.什么是minio&#xff1f; minio是一个开源的对象存储服务器&#xff0c;兼容S3协议。 官网&#xff1a;https://min.io/ 官方在开源的基础上也提供云端S3服务&#xff0c;分为个人和企业&#xff0c;有不…