├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README-en.md ├── README.md ├── assest.json ├── build.sh ├── conf ├── pproxy.conf ├── req_rewrite_8080.js └── users ├── file └── index.htm ├── fmt.sh ├── go.mod ├── go.sum ├── main.go ├── res ├── conf │ └── demo.conf ├── css │ ├── flat.css │ └── style.css ├── img │ ├── favicon.ico │ ├── folder.png │ ├── list-add.png │ ├── list.png │ ├── logo.png │ ├── text.png │ └── view.png ├── js │ ├── base64.js │ ├── default.js │ ├── jquery.js │ ├── session.js │ └── socket.io.js ├── private │ ├── client_cert.pem │ └── server_key.pem ├── sjs │ └── req_rewrite.js ├── tpl │ ├── about.html │ ├── config.html │ ├── config │ │ ├── req_demo.html │ │ └── req_form.html │ ├── error.html │ ├── file.html │ ├── file_edit.html │ ├── file_new.html │ ├── layout.html │ ├── login.html │ ├── network.html │ ├── replay.html │ ├── replay_direct.html │ └── useage.html └── version ├── script ├── create_dest_zip.sh ├── pproxy_control.sh └── windows_run.bat └── serve ├── assest.go ├── auth.go ├── broadcast.go ├── certs.go ├── config.go ├── init.go ├── kvStore.go ├── proxy.go ├── reqCtx.go ├── req_modifer.go ├── req_replay.go ├── req_rewrite.go ├── serve.go ├── sessions.go ├── util.go ├── web.go ├── web_file.go ├── wsClient.go └── wsServer.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | test/ 22 | 23 | *.exe 24 | *.test 25 | data/ 26 | dest/ 27 | hosts_* 28 | conf/* 29 | file/ 30 | .*.html 31 | .DS_Store 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2016-07-27 2 | 1. 升级依赖 3 | 4 | 2016-07-27 5 | 1. version 0.5.2 6 | 2. 修复文件上传页面错误 7 | 3. 更新lib otto ,boltdb 为最新版本 8 | 9 | 2016-03-25 10 | 1.版本号升级为0.5.1 11 | 2.改进request动态修改引擎并发异常问题 12 | 13 | 2014-12-27 14 | 1.版本号升级为0.4.7 15 | 2.添加adminPort配置项,以独立的端口提供给管理界面 16 | 17 | 2014-12-17 18 | 1.静态资源使用goassest方式而不使用之前的读取zip的方式 19 | 20 | 2014-11-08 21 | 1.重构代理处理逻辑,改进Upgrade代理协议 22 | 2.会话详情页面展现请求时间 23 | 3.upgrade结束的时候也记录一条response 以方便查看何时断开连接 24 | 25 | 2014-09-27 26 | 1.http session list support local filter 27 | 28 | 29 | 2014-09-14 30 | 1.downgrade the socket.io lib 31 | 32 | 2014-08-14 33 | 1.emit data with base64encode 34 | 2.fix some url has no schema 35 | 36 | 2014-08-10 37 | 1.websocket proxy support 38 | 39 | 2014-08-06 40 | 1.update socket.io 41 | 42 | 2014-07-19 43 | 1.修复监听端口为80时不能查看会话列表的问题 44 | 2.完善帮助说明 45 | 46 | 2014-07-15 47 | 1.get和post参数支持重写 48 | 2.重写请求出现错误自己返回502错误 49 | 50 | 2014-07-12 51 | 1.认证机制升级,新认证机制:一个ip第一次访问的时候会要求登录,若没有输入登录信息也跳过。 52 | 2.管理员用户(登录后)在session filter 输入user:any 可以查看到所有的会话信息 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 du 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | pproxy 0.5.2 2 | ====== 3 | HTTP protocol analysis tool. 4 | 5 | 6 | 7 | features: 8 |
9 | 1.url redirect 10 | redirect *http://www.baidu.com/s?wd=pproxy* to *http://m.baidu.com/s?wd=pproxy* 11 | redirect *ws://www.test.com/a* to *ws://www.example.com/b* 12 | 13 | 2.form dynamic modification 14 | get、post and header all can modify 15 | 16 | 3.hosts 17 | www.baidu.com to 127.0.0.1 18 | or www.baidu.com:81 to 192.168.1.2:8080 ,and only takes effect on port 81 19 | 20 | 4.view request and response detail 21 | form params,header and all response and easy to share 22 | 23 | 5.auth sup 24 | http Basic or only try basic auth at first request 25 | 26 | 6.replay 27 | can modify the get、post、header params and replay the request 28 | 29 | 7.parent proxy 30 | 31 |32 | 33 | use javascript code as config to modify the request params: 34 | ``` 35 | if(req.host=="www.baidu.com"){ 36 | req.host="www.163.com" 37 | req.host_addr="127.0.0.0:81" // send req to 127.0.0.1:81 38 | } 39 | ``` 40 | or: 41 | ``` 42 | if(req.host.indexOf("baidu.com")>-1){ 43 | req.host_addr="127.0.0.0:81" 44 | } 45 | ``` 46 | 47 | request params dump: 48 | ``` 49 | #url : http://www.example.com/album/list?cid=126 50 | #request has these attrs: 51 | schema : http 52 | host : www.example.com 53 | port : 80 54 | path : /album/list 55 | get: {cid:[123]} 56 | post: {} 57 | username : 58 | password : 59 | method: GET 60 | form_get : {add:function(k,v){},set:function(k,v){},get:function(k){},len:function(){}} 61 | form_post : {add:function(k,v){},set:function(k,v){},get:function(k){},len:function(){}} 62 | 63 | host_addr: #modify hosts eg:127.0.0.1:3218 64 | 65 | #note get and post value is array 66 | #form_get: helper function for get params 67 | #form_post: helper function for post params 68 | ``` 69 | 70 | 71 | hosts config demo: 72 | ``` 73 | www.baidu.com 127.0.0.1 74 | www.baidu.com:81 10.0.2.2:8080 75 | ``` 76 | 77 | disable req_rewrite.js 78 | first line ```//ignore``` 79 | 80 | 81 | req_rewrite.js支持不同用户设置不同的规则。默认使用当前验证使用用户名的规则,若无则使用默认的。 82 | 83 | configs: 84 | ``` 85 | conf/ 86 | ├── pproxy.conf #server config 87 | ├── hosts_8080 #hosts for 8080 88 | ├── req_rewrite_8080.js #8080端口server的url重写规则 89 | ├── hosts_8081 90 | ├── req_rewrite_8081.js 91 | └── users #全局帐号配置文件 92 | ``` 93 | 94 | users配置: 95 | ``` 96 | #帐号 admin,密码 是 psw,是管理员帐号 97 | name:admin psw:psw is_admin:admin 98 | 99 | #密码也可以存储为md5值,使用 psw_md5:32位md密码 100 | name:admin_sec psw_md5:7bb483729b5a8e26f73e1831cde5b842 is_admin:admin 101 | ``` 102 | 可以在线修改配置时必须使用管理员帐号登录 103 | 104 | 配置文件示例: 105 | ``` 106 | 107 | port : 8080 108 | 109 | title : demo 110 | notice :notice notice 111 | 112 | #数据存放目录,相对于当前配置的路径 113 | dataDir : ../data/ 114 | 115 | #数据存放天数,0为永久存储(目前只在重启的时候会进行数据清理) 116 | dataStoreDay : 15 117 | 118 | #代理服务认证方式 119 | #options:{none : 无认证,basic:http basic ,basic_try:尝试httpBasic认证 ,basic_any:任意帐号} 120 | authType : none 121 | 122 | #那些request和response数据进行存储 123 | #options:{ all : 所有 only_broadcast : 发送到session list的才存储} 124 | responseSave : only_broadcast 125 | 126 | #session列表查看数据 127 | # options :{ all:所有人可见 ip_or_user : 输入正确的ip或者user后可见} 128 | sessionView : all 129 | 130 | #父级代理 131 | #eg http://10.10.2.2:3128 or http://name:psw@10.10.2.2:3128 132 | # http://pass:pass@10.10.2.2:3128 the user and psw will pass through to the parent proxy 133 | parentProxy: 134 | ``` 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pproxy 0.5.2 2 | ====== 3 | ## intro 4 | HTTP 协议抓包代理程序, HTTP 协议调试工具。 5 | 采用 Go 编写,采用 BS 模式(s-代理程序,b-会话查看、配置管理等功能) 6 | 7 | 0.4.2版本已经支持websocket代理,以及重定向(和普通http请求一样使用) 8 | 9 | 0.5 版本是对底层存储进行了替换,并且尝试支持https抓包 10 | 11 | ## install 12 | 13 | 已经安装go的用户直接安装: 14 | >go install github.com/hidu/pproxy@master 15 | 16 | ## 功能特性 17 |
18 | 1.url重定向 19 | 如把 http://www.baidu.com/s?wd=pproxy 修改为 http://m.baidu.com/s?wd=pproxy 20 | 或者把 ws://www.test.com/a 重定向到 ws://www.example.com/b 21 | 22 | 2.form表单动态修改 23 | get、post可以动态修改(增删改) 24 | 25 | 3.hosts文件支持 26 | 相当于 修改host或者dns 如 27 | 将www.baidu.com 请求全部发往127.0.0.1 28 | 将www.baidu.com:81 请求全部发往192.168.1.2:8080 29 | 30 | 4.可查看request 和response详情 31 | form表单参数,header等都可以很方便的看到 32 | 33 | 5.登录认证支持 34 | 支持httpBasic认证 35 | 36 | 6.replay功能 37 | 可以修改request的参数(get、post、header) 38 | 39 | 7.父级代理 40 | 41 |42 | 43 | ## 配置 44 | 45 | ### rewrite req 46 | 使用javascript来配置重定向功能,如 47 | ``` 48 | if(req.host=="www.baidu.com"){ 49 | req.host="www.163.com" 50 | req.host_addr="127.0.0.0:81" // send req to 127.0.0.1:81 51 | } 52 | ``` 53 | 当然也可以这样: 54 | ``` 55 | if(req.host.indexOf("baidu.com")>-1){ 56 | req.host_addr="127.0.0.0:81" 57 | } 58 | ``` 59 | 60 | ### req变量示例 61 | ``` 62 | #url : http://www.example.com/album/list?cid=126 63 | #req对象有如下一下属性: 64 | schema : http 65 | host : www.example.com 66 | port : 80 67 | path : /album/list 68 | get: {cid:[123]} 69 | post: {} 70 | username : 71 | password : 72 | method: GET 73 | form_get : {add:function(k,v){},set:function(k,v){},get:function(k){},len:function(){}} 74 | form_post : {add:function(k,v){},set:function(k,v){},get:function(k){},len:function(){}} 75 | 76 | host_addr: #修改该请求的host是使用,如 127.0.0.1:3218 77 | 78 | #注意 get 和post的值是数组,如上cid参数 79 | #form_get 用于更方便的操作 get参数对象 80 | #form_post 用于更方便的操作 post参数对象 81 | ``` 82 | 83 | ### hosts 84 | 增强的hosts文件使用: 85 | ``` 86 | www.baidu.com 127.0.0.1 87 | www.baidu.com:81 10.0.2.2:8080 88 | ``` 89 | 90 | ### other 91 | 忽略禁用req_rewrite.js 92 | 在js文件的第一行内容写入 ```//ignore``` 93 | 94 | req_rewrite.js支持不同用户设置不同的规则。默认使用当前验证使用用户名的规则,若无则使用默认的。 95 | 96 | ### 配置文件结构: 97 | ``` 98 | conf/ 99 | ├── pproxy.conf #server的配置 100 | ├── hosts_8080 #8080端口server的hosts规则 101 | ├── req_rewrite_8080.js #8080端口server的url重写规则 102 | ├── hosts_8081 103 | ├── req_rewrite_8081.js 104 | └── users #全局帐号配置文件 105 | ``` 106 | 107 | ### users配置: 108 | ``` 109 | #帐号 admin,密码 是 psw,是管理员帐号 110 | name:admin psw:psw is_admin:admin 111 | 112 | #密码也可以存储为md5值,使用 psw_md5:32位md密码 113 | name:admin_sec psw_md5:7bb483729b5a8e26f73e1831cde5b842 is_admin:admin 114 | ``` 115 | 可以在线修改配置时必须使用管理员帐号登录 116 | 117 | ### 配置文件示例(pproxy.conf): 118 | ``` 119 | #提供代理服务的端口 120 | port : 8080 121 | 122 | #管理界面的端口,为0表示和代理服务使用相同的端口,eg:8081 123 | adminPort : 0 124 | 125 | title : demo 126 | notice :notice notice 127 | 128 | #数据存放目录,相对于当前配置的路径 129 | dataDir : ../data/ 130 | 131 | #数据存放天数,0为永久存储(目前只在重启的时候会进行数据清理) 132 | dataStoreDay : 15 133 | 134 | #代理服务认证方式 135 | #options:{none : 无认证,basic:http basic ,basic_try:尝试httpBasic认证 ,basic_any:任意帐号} 136 | authType : none 137 | 138 | #那些request和response数据进行存储 139 | #options:{ all : 所有 only_broadcast : 发送到session list的才存储} 140 | responseSave : only_broadcast 141 | 142 | #session列表查看数据 143 | # options :{ all:所有人可见 ip_or_user : 输入正确的ip或者user后可见} 144 | sessionView : all 145 | 146 | #父级代理 147 | #eg http://10.10.2.2:3128 or http://name:psw@10.10.2.2:3128 148 | # http://pass:pass@10.10.2.2:3128 the user and psw will pass through to the parent proxy 149 | parentProxy: 150 | 151 | 152 | #是否使用中间人方式对https进行抓包,若启用的话 需要客户端按照证书-/res/private/client_cert.pem 153 | #pproxy内置默认证书存放在/res/private目录中 154 | #options:{on:启用 off:禁用} 155 | ssl : on 156 | 157 | #ssl 服务端秘钥文件地址,为空则使用默认内置的 /res/private/server_key.pem 158 | ssl_server_key: 159 | #ssl 公钥地址 ,为空则使用默认内置的 /res/private/client_cert.pem 160 | ssl_client_cert : 161 | ``` 162 | 163 | ## (管理)web查看界面 164 | 方式1: 直接访问 http://serverHost:port 165 | 方式2: 直接访问 http://serverHost:adminPort 166 | 方式3: 浏览器设置http代理 serverHost:port,访问 http://pproxy.man 或者 http://pproxy.com 167 | 168 | # 其他 169 | ## 如何自己修改源码中的静态资源? 170 | 该项目的静态资源(res目录中的所有内容)都编译到go文件中去了,可以处理即可: 171 | 1. 安装goassest工具: 172 | ``` 173 | go get -u github.com/hidu/goassest 174 | ``` 175 | 2.到pproxy代码根目录下运行命令: 176 | ``` 177 | goassest 178 | ``` 179 | 180 | 调试过程中可以添加参数 `-assest_direct` 可以让静态资源实时生效而不需要重新编译静态资源: 181 | ``` 182 | go run proxy_main.go -assest_direct 183 | ``` 184 | -------------------------------------------------------------------------------- /assest.json: -------------------------------------------------------------------------------- 1 | { 2 | "src":"res", 3 | "dest":"serve/assest.go", 4 | "package":"serve" 5 | } 6 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # build for window: build.sh windows 3 | # default linux 4 | #./gox -build-toolchain 5 | 6 | set -e 7 | cd $(dirname $0) 8 | 9 | #export GOPATH=`readlink -f Godeps/_workspace`:$GOPATH 10 | 11 | export GO15VENDOREXPERIMENT=1 12 | 13 | go install -------------------------------------------------------------------------------- /conf/pproxy.conf: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | # pproxy demo conf # 3 | ########################################################## 4 | 5 | #提供代理服务的端口 6 | port : 8080 7 | 8 | #管理界面的端口,为0表示和代理服务使用相同的端口 9 | adminPort : 0 10 | 11 | title : hello pproxy 12 | notice : notice notice 13 | 14 | #数据存放目录,相对于当前配置的路径 15 | dataDir : ../data/ 16 | 17 | #静态文件存放目录 18 | fileDir: ../file/ 19 | 20 | 21 | #数据存放天数,0为永久存储 22 | dataStoreDay : 15 23 | 24 | #代理服务认证方式 25 | #options:{none : 无认证,basic:http basic ,basic_try:尝试httpBasic认证 ,basic_any:任意帐号} 26 | authType : none 27 | 28 | #那些request和response数据进行存储 29 | #options:{ all : 所有 only_broadcast : 发送到session list的才存储} 30 | responseSave : only_broadcast 31 | 32 | #session列表查看数据 33 | # options :{ all:所有人可见 ip_or_user : 输入正确的ip或者user后可见} 34 | sessionView : all 35 | 36 | #eg http://10.10.2.2:3128 or http://name:psw@10.10.2.2:3128 37 | # http://name:psw@10.10.2.2:3128 the user and psw will pass through to the parent proxy 38 | parentProxy: 39 | 40 | #是否使用中间人方式对https进行抓包,若启用的话 需要客户端按照证书-/res/private/client_cert.pem 41 | #pproxy内置默认证书存放在/res/private目录中 42 | #options:{on:启用 off:禁用} 43 | ssl : off 44 | 45 | #ssl 服务端秘钥文件地址,为空则使用默认内置的 /res/private/server_key.pem 46 | ssl_server_key: 47 | #ssl 公钥地址 ,为空则使用默认内置的 /res/private/client_cert.pem 48 | ssl_client_cert : 49 | 50 | #是否开启动态修改请求的功能 {on:开启,off:禁止} 51 | modifyRequest:on -------------------------------------------------------------------------------- /conf/req_rewrite_8080.js: -------------------------------------------------------------------------------- 1 | if (req.host == "news.163.com") { 2 | req.host = "news.baidu.com" 3 | req.host_addr = "127.0.0.1:80" 4 | req.path = "/h/g.php" 5 | form_get.add("ga", "aaa") 6 | form_post.set("a", "ddd") 7 | req.post["d"] = "ddd" 8 | req.post["c"] = 123 9 | } 10 | if (req.host == "news.baidu.com" && req.schema=="ws") { 11 | req.host_addr="127.0.0.1:23456" 12 | } 13 | if(req.host=="www.hao123.com"){ 14 | req.url="http://127.0.0.1/" 15 | } 16 | 17 | if(req.host=="www.oschina.net"){ 18 | //use_file("index.htm") 19 | use_file("http://127.0.0.1:8080/f/index.htm") 20 | } -------------------------------------------------------------------------------- /conf/users: -------------------------------------------------------------------------------- 1 | name:admin psw:psw is_admin:admin 2 | name:abc psw:abc 3 | name:a psw:a -------------------------------------------------------------------------------- /file/index.htm: -------------------------------------------------------------------------------- 1 | hello pproxy < " -------------------------------------------------------------------------------- /fmt.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | cd $(dirname $0) 3 | cd serve 4 | #gofmt -tabs=false -w=true -tabwidth=4 . 5 | gofmt -w=true -s=true . 6 | 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hidu/pproxy 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/Unknwon/goconfig v1.0.0 7 | github.com/boltdb/bolt v1.3.1 8 | github.com/elazarl/goproxy v0.0.0-20241219141958-0cbc93263399 9 | github.com/googollee/go-socket.io v0.9.1 10 | github.com/hidu/goutils v0.0.2 11 | github.com/robertkrimen/otto v0.5.1 12 | ) 13 | 14 | require ( 15 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect 16 | github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac // indirect 17 | github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 // indirect 18 | golang.org/x/net v0.33.0 // indirect 19 | golang.org/x/sys v0.28.0 // indirect 20 | golang.org/x/text v0.21.0 // indirect 21 | gopkg.in/sourcemap.v1 v1.0.5 // indirect 22 | ) 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Unknwon/goconfig v1.0.0 h1:9IAu/BYbSLQi8puFjUQApZTxIHqSwrj5d8vpP8vTq4A= 2 | github.com/Unknwon/goconfig v1.0.0/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw= 3 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= 4 | github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= 5 | github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= 6 | github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= 7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 10 | github.com/elazarl/goproxy v0.0.0-20241219141958-0cbc93263399 h1:WBf0ImRm78m/cgOOob9PUyQdYYod2luQA6fs+OqAjbw= 11 | github.com/elazarl/goproxy v0.0.0-20241219141958-0cbc93263399/go.mod h1:3tPvP6c6GrQS4u4TJEbOoqGC2wDNMLra3t6AXWwtL0M= 12 | github.com/elazarl/goproxy/ext v0.0.0-20241217120900-7711dfa3811c h1:R+i10jtNSzKJKqEZAYJnR9M8y14k0zrNHqD1xkv/A2M= 13 | github.com/elazarl/goproxy/ext v0.0.0-20241217120900-7711dfa3811c/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= 14 | github.com/googollee/go-socket.io v0.9.1 h1:KYsu63c3H5SaeQ3MDlHSTE/LJnwok2SH1M5wy4ZaYD0= 15 | github.com/googollee/go-socket.io v0.9.1/go.mod h1:Q0CvnKmaZNgDXIi85at4eLadAOS1hWDLaDATQpuH3i4= 16 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 17 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg= 18 | github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 19 | github.com/hidu/goutils v0.0.2 h1:ZjwXZhuWXZjzQ4dHoFo2iN6oDXH1TCudQZ0V2vdAUfQ= 20 | github.com/hidu/goutils v0.0.2/go.mod h1:m13DejGt6FVHM+taWpMHpavxBRZnnQBZeDJyB/YsyRI= 21 | github.com/howeyc/fsnotify v0.9.0/go.mod h1:41HzSPxBGeFRQKEEwgh49TRw/nKBsYZ2cF1OzPjSJsA= 22 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 23 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 24 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 25 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 26 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 27 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 28 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 29 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 30 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 31 | github.com/robertkrimen/otto v0.5.1 h1:avDI4ToRk8k1hppLdYFTuuzND41n37vPGJU7547dGf0= 32 | github.com/robertkrimen/otto v0.5.1/go.mod h1:bS433I4Q9p+E5pZLu7r17vP6FkE6/wLxBdmKjoqJXF8= 33 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 34 | github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg= 35 | github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 36 | github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= 37 | github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 38 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 39 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 40 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 41 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 42 | github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= 43 | github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= 44 | github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= 45 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 46 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 47 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 48 | golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= 49 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= 50 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 51 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 52 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 53 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 54 | golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 55 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= 56 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 57 | gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= 58 | gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= 59 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 60 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 61 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | 9 | "github.com/hidu/pproxy/serve" 10 | ) 11 | 12 | var configPath = flag.String("conf", "./conf/pproxy.conf", "pproxy's config file") 13 | var port = flag.Int("port", 0, "proxy port") 14 | var vv = flag.Bool("vv", false, "debug,log request with more detail") 15 | var showConf = flag.Bool("demo_conf", false, "show default conf") 16 | 17 | var version = flag.Bool("v", false, "show version") 18 | 19 | func init() { 20 | df := flag.Usage 21 | 22 | flag.Usage = func() { 23 | df() 24 | fmt.Fprintln(os.Stderr, "\n HTTP protocol analysis tool\n https://github.com/hidu/pproxy/\n") 25 | } 26 | } 27 | 28 | func main() { 29 | flag.Parse() 30 | 31 | if *showConf { 32 | demoConf := serve.GetDemoConf() 33 | fmt.Println(demoConf) 34 | os.Exit(0) 35 | } 36 | 37 | if *version { 38 | fmt.Println("pproxy version:", serve.GetVersion()) 39 | os.Exit(0) 40 | } 41 | 42 | log.SetFlags(log.Lshortfile | log.LstdFlags | log.Ldate) 43 | ser, err := serve.NewProxyServe(*configPath, *port) 44 | if err != nil { 45 | fmt.Println("start pproxy failed", err) 46 | os.Exit(2) 47 | } 48 | ser.Debug = *vv 49 | ser.Start() 50 | } 51 | -------------------------------------------------------------------------------- /res/conf/demo.conf: -------------------------------------------------------------------------------- 1 | ########################################################## 2 | # pproxy demo conf # 3 | ########################################################## 4 | 5 | #提供代理服务的端口 6 | port : 8080 7 | 8 | #管理界面的端口,为0表示和代理服务使用相同的端口 9 | adminPort : 0 10 | 11 | title : hello pproxy 12 | notice : notice notice 13 | 14 | #数据存放目录,相对于当前配置的路径 15 | dataDir : ../data/ 16 | 17 | #静态文件存放目录 18 | fileDir: ../file/ 19 | 20 | 21 | #数据存放天数,0为永久存储(目前只在重启的时候会进行数据清理) 22 | dataStoreDay : 15 23 | 24 | #代理服务认证方式 25 | #options:{none : 无认证,basic:http basic ,basic_try:尝试httpBasic认证 ,basic_any:任意帐号} 26 | authType : none 27 | 28 | #那些request和response数据进行存储 29 | #options:{ all : 所有 only_broadcast : 发送到session list的才存储} 30 | responseSave : only_broadcast 31 | 32 | #session列表查看数据 33 | # options :{ all:所有人可见 ip_or_user : 输入正确的ip或者user后可见} 34 | sessionView : all 35 | 36 | #eg http://10.10.2.2:3128 or http://name:psw@10.10.2.2:3128 37 | # http://name:psw@10.10.2.2:3128 the user and psw will pass through to the parent proxy 38 | parentProxy: -------------------------------------------------------------------------------- /res/css/flat.css: -------------------------------------------------------------------------------- 1 | .dropdown-arrow-inverse { 2 | border-bottom-color: #34495e !important; 3 | border-top-color: #34495e !important; 4 | } 5 | a { 6 | color: #428bca; 7 | text-decoration: none; 8 | -webkit-transition: 0.25s; 9 | transition: 0.25s; 10 | } 11 | a:hover, 12 | a:focus { 13 | color: #428bca; 14 | text-decoration: none; 15 | } 16 | a:focus { 17 | outline: none; 18 | } 19 | .img-rounded { 20 | border-radius: 6px; 21 | } 22 | .img-thumbnail { 23 | padding: 4px; 24 | line-height: 1.72222; 25 | background-color: #ffffff; 26 | border: 2px solid #bdc3c7; 27 | border-radius: 6px; 28 | -webkit-transition: all 0.25s ease-in-out; 29 | transition: all 0.25s ease-in-out; 30 | display: inline-block; 31 | max-width: 100%; 32 | height: auto; 33 | } 34 | .img-comment { 35 | font-size: 15px; 36 | line-height: 1.2; 37 | font-style: italic; 38 | margin: 24px 0; 39 | } 40 | h1, 41 | h2, 42 | h3, 43 | h4, 44 | h5, 45 | h6, 46 | .h1, 47 | .h2, 48 | .h3, 49 | .h4, 50 | .h5, 51 | .h6 { 52 | font-family: inherit; 53 | font-weight: 700; 54 | line-height: 1.1; 55 | color: inherit; 56 | } 57 | h1 small, 58 | h2 small, 59 | h3 small, 60 | h4 small, 61 | h5 small, 62 | h6 small, 63 | .h1 small, 64 | .h2 small, 65 | .h3 small, 66 | .h4 small, 67 | .h5 small, 68 | .h6 small { 69 | color: #e7e9ec; 70 | } 71 | h1, 72 | h2, 73 | h3 { 74 | margin-top: 30px; 75 | margin-bottom: 15px; 76 | } 77 | h4, 78 | h5, 79 | h6 { 80 | margin-top: 15px; 81 | margin-bottom: 15px; 82 | } 83 | h6 { 84 | font-weight: normal; 85 | } 86 | h1, 87 | .h1 { 88 | font-size: 61px; 89 | } 90 | h2, 91 | .h2 { 92 | font-size: 53px; 93 | } 94 | h3, 95 | .h3 { 96 | font-size: 40px; 97 | } 98 | h4, 99 | .h4 { 100 | font-size: 29px; 101 | } 102 | h5, 103 | .h5 { 104 | font-size: 28px; 105 | } 106 | h6, 107 | .h6 { 108 | font-size: 24px; 109 | } 110 | p { 111 | font-size: 18px; 112 | line-height: 1.72222; 113 | margin: 0 0 15px; 114 | } 115 | .lead { 116 | margin-bottom: 30px; 117 | font-size: 28px; 118 | line-height: 1.46428571; 119 | font-weight: 300; 120 | } 121 | @media (min-width: 768px) { 122 | .lead { 123 | font-size: 30.006px; 124 | } 125 | } 126 | small, 127 | .small { 128 | font-size: 83%; 129 | line-height: 2.067; 130 | } 131 | .text-muted { 132 | color: #bdc3c7; 133 | } 134 | .text-inverse { 135 | color: #ffffff; 136 | } 137 | .text-primary { 138 | color: #428bca; 139 | } 140 | a.text-primary:hover { 141 | color: #15967d; 142 | } 143 | .text-warning { 144 | color: #f1c40f; 145 | } 146 | a.text-warning:hover { 147 | color: #c19d0c; 148 | } 149 | .text-danger { 150 | color: #e74c3c; 151 | } 152 | a.text-danger:hover { 153 | color: #b93d30; 154 | } 155 | .text-success { 156 | color: #2ecc71; 157 | } 158 | a.text-success:hover { 159 | color: #25a35a; 160 | } 161 | .text-info { 162 | color: #3498db; 163 | } 164 | a.text-info:hover { 165 | color: #2a7aaf; 166 | } 167 | .bg-primary { 168 | color: #ffffff; 169 | background-color: #34495e; 170 | } 171 | a.bg-primary:hover { 172 | background-color: #222f3d; 173 | } 174 | .bg-success { 175 | background-color: #dff0d8; 176 | } 177 | a.bg-success:hover { 178 | background-color: #c1e2b3; 179 | } 180 | .bg-info { 181 | background-color: #d9edf7; 182 | } 183 | a.bg-info:hover { 184 | background-color: #afd9ee; 185 | } 186 | .bg-warning { 187 | background-color: #fcf8e3; 188 | } 189 | a.bg-warning:hover { 190 | background-color: #f7ecb5; 191 | } 192 | .bg-danger { 193 | background-color: #f2dede; 194 | } 195 | a.bg-danger:hover { 196 | background-color: #e4b9b9; 197 | } 198 | .page-header { 199 | padding-bottom: 14px; 200 | margin: 60px 0 30px; 201 | border-bottom: 1px solid #e7e9ec; 202 | } 203 | ul, 204 | ol { 205 | margin-bottom: 15px; 206 | } 207 | dl { 208 | margin-bottom: 30px; 209 | } 210 | dt, 211 | dd { 212 | line-height: 1.72222; 213 | } 214 | @media (min-width: 768px) { 215 | .dl-horizontal dt { 216 | width: 160px; 217 | } 218 | .dl-horizontal dd { 219 | margin-left: 180px; 220 | } 221 | } 222 | abbr[title], 223 | abbr[data-original-title] { 224 | border-bottom: 1px dotted #bdc3c7; 225 | } 226 | blockquote { 227 | border-left: 3px solid #e7e9ec; 228 | padding: 0 0 0 16px; 229 | margin: 0 0 30px; 230 | } 231 | blockquote p { 232 | font-size: 20px; 233 | line-height: 1.55; 234 | font-weight: normal; 235 | margin-bottom: .4em; 236 | } 237 | blockquote small, 238 | blockquote .small { 239 | font-size: 18px; 240 | line-height: 1.72222; 241 | font-style: italic; 242 | color: inherit; 243 | } 244 | blockquote small:before, 245 | blockquote .small:before { 246 | content: ""; 247 | } 248 | blockquote.pull-right { 249 | padding-right: 16px; 250 | padding-left: 0; 251 | border-right: 3px solid #e7e9ec; 252 | border-left: 0; 253 | } 254 | blockquote.pull-right small:after { 255 | content: ""; 256 | } 257 | address { 258 | margin-bottom: 30px; 259 | line-height: 1.72222; 260 | } 261 | code, 262 | kbd, 263 | pre, 264 | samp { 265 | font-family: Monaco, Menlo, Consolas, "Courier New", monospace; 266 | } 267 | code { 268 | padding: 2px 6px; 269 | font-size: 85%; 270 | color: #c7254e; 271 | background-color: #f9f2f4; 272 | border-radius: 4px; 273 | } 274 | kbd { 275 | padding: 2px 6px; 276 | font-size: 85%; 277 | color: #ffffff; 278 | background-color: #34495e; 279 | border-radius: 4px; 280 | box-shadow: none; 281 | } 282 | pre { 283 | padding: 8px; 284 | margin: 0 0 15px; 285 | font-size: 13px; 286 | line-height: 1.72222; 287 | color: inherit; 288 | background-color: #ffffff; 289 | border: 2px solid #e7e9ec; 290 | border-radius: 6px; 291 | white-space: pre; 292 | } 293 | .pre-scrollable { 294 | max-height: 340px; 295 | } 296 | .btn { 297 | border: none; 298 | font-size: 14px; 299 | font-weight: normal; 300 | cursor: pointer; 301 | line-height: 1.4; 302 | border-radius: 4px; 303 | padding: 6px 12px; 304 | -webkit-font-smoothing: subpixel-antialiased; 305 | -webkit-transition: border .25s linear, color .25s linear, background-color .25s linear; 306 | transition: border .25s linear, color .25s linear, background-color .25s linear; 307 | } 308 | .btn:hover, 309 | .btn:focus { 310 | outline: none; 311 | color: #ffffff; 312 | } 313 | .btn:active, 314 | .btn.active { 315 | outline: none; 316 | -webkit-box-shadow: none; 317 | box-shadow: none; 318 | } 319 | .btn.disabled, 320 | .btn[disabled], 321 | fieldset[disabled] .btn { 322 | background-color: #bdc3c7; 323 | color: rgba(255, 255, 255, 0.75); 324 | opacity: 0.7; 325 | filter: alpha(opacity=70); 326 | } 327 | .btn > [class^="fui-"] { 328 | margin: 0 1px; 329 | position: relative; 330 | line-height: 1; 331 | top: 1px; 332 | } 333 | .btn-xs.btn > [class^="fui-"] { 334 | font-size: 11px; 335 | top: 0; 336 | } 337 | .btn-hg.btn > [class^="fui-"] { 338 | top: 2px; 339 | } 340 | .btn-default { 341 | color: #ffffff; 342 | background-color: #bdc3c7; 343 | } 344 | .btn-default:hover, 345 | .btn-default:focus, 346 | .btn-default:active, 347 | .btn-default.active, 348 | .open .dropdown-toggle.btn-default { 349 | color: #ffffff; 350 | background-color: #cacfd2; 351 | border-color: #cacfd2; 352 | } 353 | .btn-default:active, 354 | .btn-default.active, 355 | .open .dropdown-toggle.btn-default { 356 | background: #a1a6a9; 357 | border-color: #a1a6a9; 358 | } 359 | .btn-default.disabled, 360 | .btn-default[disabled], 361 | fieldset[disabled] .btn-default, 362 | .btn-default.disabled:hover, 363 | .btn-default[disabled]:hover, 364 | fieldset[disabled] .btn-default:hover, 365 | .btn-default.disabled:focus, 366 | .btn-default[disabled]:focus, 367 | fieldset[disabled] .btn-default:focus, 368 | .btn-default.disabled:active, 369 | .btn-default[disabled]:active, 370 | fieldset[disabled] .btn-default:active, 371 | .btn-default.disabled.active, 372 | .btn-default[disabled].active, 373 | fieldset[disabled] .btn-default.active { 374 | background-color: #bdc3c7; 375 | border-color: #bdc3c7; 376 | } 377 | .btn-primary { 378 | color: #ffffff; 379 | background-color: #428bca; 380 | } 381 | .btn-primary:hover, 382 | .btn-primary:focus, 383 | .btn-primary:active, 384 | .btn-primary.active, 385 | .open .dropdown-toggle.btn-primary { 386 | color: #ffffff; 387 | background-color: #3276b1; 388 | border-color: #3276b1; 389 | } 390 | .btn-primary:active, 391 | .btn-primary.active, 392 | .open .dropdown-toggle.btn-primary { 393 | background: #428bca; 394 | border-color: #428bca; 395 | } 396 | .btn-primary.disabled, 397 | .btn-primary[disabled], 398 | fieldset[disabled] .btn-primary, 399 | .btn-primary.disabled:hover, 400 | .btn-primary[disabled]:hover, 401 | fieldset[disabled] .btn-primary:hover, 402 | .btn-primary.disabled:focus, 403 | .btn-primary[disabled]:focus, 404 | fieldset[disabled] .btn-primary:focus, 405 | .btn-primary.disabled:active, 406 | .btn-primary[disabled]:active, 407 | fieldset[disabled] .btn-primary:active, 408 | .btn-primary.disabled.active, 409 | .btn-primary[disabled].active, 410 | fieldset[disabled] .btn-primary.active { 411 | background-color: #428bca; 412 | border-color: #428bca; 413 | } 414 | .btn-info { 415 | color: #ffffff; 416 | background-color: #3498db; 417 | } 418 | .btn-info:hover, 419 | .btn-info:focus, 420 | .btn-info:active, 421 | .btn-info.active, 422 | .open .dropdown-toggle.btn-info { 423 | color: #ffffff; 424 | background-color: #5dade2; 425 | border-color: #5dade2; 426 | } 427 | .btn-info:active, 428 | .btn-info.active, 429 | .open .dropdown-toggle.btn-info { 430 | background: #2c81ba; 431 | border-color: #2c81ba; 432 | } 433 | .btn-info.disabled, 434 | .btn-info[disabled], 435 | fieldset[disabled] .btn-info, 436 | .btn-info.disabled:hover, 437 | .btn-info[disabled]:hover, 438 | fieldset[disabled] .btn-info:hover, 439 | .btn-info.disabled:focus, 440 | .btn-info[disabled]:focus, 441 | fieldset[disabled] .btn-info:focus, 442 | .btn-info.disabled:active, 443 | .btn-info[disabled]:active, 444 | fieldset[disabled] .btn-info:active, 445 | .btn-info.disabled.active, 446 | .btn-info[disabled].active, 447 | fieldset[disabled] .btn-info.active { 448 | background-color: #3498db; 449 | border-color: #3498db; 450 | } 451 | .btn-danger { 452 | color: #ffffff; 453 | background-color: #e74c3c; 454 | } 455 | .btn-danger:hover, 456 | .btn-danger:focus, 457 | .btn-danger:active, 458 | .btn-danger.active, 459 | .open .dropdown-toggle.btn-danger { 460 | color: #ffffff; 461 | background-color: #ec7063; 462 | border-color: #ec7063; 463 | } 464 | .btn-danger:active, 465 | .btn-danger.active, 466 | .open .dropdown-toggle.btn-danger { 467 | background: #c44133; 468 | border-color: #c44133; 469 | } 470 | .btn-danger.disabled, 471 | .btn-danger[disabled], 472 | fieldset[disabled] .btn-danger, 473 | .btn-danger.disabled:hover, 474 | .btn-danger[disabled]:hover, 475 | fieldset[disabled] .btn-danger:hover, 476 | .btn-danger.disabled:focus, 477 | .btn-danger[disabled]:focus, 478 | fieldset[disabled] .btn-danger:focus, 479 | .btn-danger.disabled:active, 480 | .btn-danger[disabled]:active, 481 | fieldset[disabled] .btn-danger:active, 482 | .btn-danger.disabled.active, 483 | .btn-danger[disabled].active, 484 | fieldset[disabled] .btn-danger.active { 485 | background-color: #e74c3c; 486 | border-color: #e74c3c; 487 | } 488 | .btn-success { 489 | color: #ffffff; 490 | background-color: #2ecc71; 491 | } 492 | .btn-success:hover, 493 | .btn-success:focus, 494 | .btn-success:active, 495 | .btn-success.active, 496 | .open .dropdown-toggle.btn-success { 497 | color: #ffffff; 498 | background-color: #58d68d; 499 | border-color: #58d68d; 500 | } 501 | .btn-success:active, 502 | .btn-success.active, 503 | .open .dropdown-toggle.btn-success { 504 | background: #27ad60; 505 | border-color: #27ad60; 506 | } 507 | .btn-success.disabled, 508 | .btn-success[disabled], 509 | fieldset[disabled] .btn-success, 510 | .btn-success.disabled:hover, 511 | .btn-success[disabled]:hover, 512 | fieldset[disabled] .btn-success:hover, 513 | .btn-success.disabled:focus, 514 | .btn-success[disabled]:focus, 515 | fieldset[disabled] .btn-success:focus, 516 | .btn-success.disabled:active, 517 | .btn-success[disabled]:active, 518 | fieldset[disabled] .btn-success:active, 519 | .btn-success.disabled.active, 520 | .btn-success[disabled].active, 521 | fieldset[disabled] .btn-success.active { 522 | background-color: #2ecc71; 523 | border-color: #2ecc71; 524 | } 525 | .btn-warning { 526 | color: #ffffff; 527 | background-color: #f1c40f; 528 | } 529 | .btn-warning:hover, 530 | .btn-warning:focus, 531 | .btn-warning:active, 532 | .btn-warning.active, 533 | .open .dropdown-toggle.btn-warning { 534 | color: #ffffff; 535 | background-color: #f4d313; 536 | border-color: #f4d313; 537 | } 538 | .btn-warning:active, 539 | .btn-warning.active, 540 | .open .dropdown-toggle.btn-warning { 541 | background: #cda70d; 542 | border-color: #cda70d; 543 | } 544 | .btn-warning.disabled, 545 | .btn-warning[disabled], 546 | fieldset[disabled] .btn-warning, 547 | .btn-warning.disabled:hover, 548 | .btn-warning[disabled]:hover, 549 | fieldset[disabled] .btn-warning:hover, 550 | .btn-warning.disabled:focus, 551 | .btn-warning[disabled]:focus, 552 | fieldset[disabled] .btn-warning:focus, 553 | .btn-warning.disabled:active, 554 | .btn-warning[disabled]:active, 555 | fieldset[disabled] .btn-warning:active, 556 | .btn-warning.disabled.active, 557 | .btn-warning[disabled].active, 558 | fieldset[disabled] .btn-warning.active { 559 | background-color: #f1c40f; 560 | border-color: #f1c40f; 561 | } 562 | .btn-inverse { 563 | color: #ffffff; 564 | background-color: #34495e; 565 | } 566 | .btn-inverse:hover, 567 | .btn-inverse:focus, 568 | .btn-inverse:active, 569 | .btn-inverse.active, 570 | .open .dropdown-toggle.btn-inverse { 571 | color: #ffffff; 572 | background-color: #415b76; 573 | border-color: #415b76; 574 | } 575 | .btn-inverse:active, 576 | .btn-inverse.active, 577 | .open .dropdown-toggle.btn-inverse { 578 | background: #2c3e50; 579 | border-color: #2c3e50; 580 | } 581 | .btn-inverse.disabled, 582 | .btn-inverse[disabled], 583 | fieldset[disabled] .btn-inverse, 584 | .btn-inverse.disabled:hover, 585 | .btn-inverse[disabled]:hover, 586 | fieldset[disabled] .btn-inverse:hover, 587 | .btn-inverse.disabled:focus, 588 | .btn-inverse[disabled]:focus, 589 | fieldset[disabled] .btn-inverse:focus, 590 | .btn-inverse.disabled:active, 591 | .btn-inverse[disabled]:active, 592 | fieldset[disabled] .btn-inverse:active, 593 | .btn-inverse.disabled.active, 594 | .btn-inverse[disabled].active, 595 | fieldset[disabled] .btn-inverse.active { 596 | background-color: #34495e; 597 | border-color: #34495e; 598 | } 599 | .btn-embossed { 600 | -webkit-box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.15); 601 | box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.15); 602 | } 603 | .btn-embossed.active, 604 | .btn-embossed:active { 605 | -webkit-box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.15); 606 | box-shadow: inset 0 2px 0 rgba(0, 0, 0, 0.15); 607 | } 608 | .btn-wide { 609 | min-width: 140px; 610 | padding-left: 30px; 611 | padding-right: 30px; 612 | } 613 | .btn-link { 614 | color: #428bca; 615 | } 616 | .btn-link:hover, 617 | .btn-link:focus { 618 | color: #428bca; 619 | text-decoration: underline; 620 | background-color: transparent; 621 | } 622 | .btn-link[disabled]:hover, 623 | fieldset[disabled] .btn-link:hover, 624 | .btn-link[disabled]:focus, 625 | fieldset[disabled] .btn-link:focus { 626 | color: #bdc3c7; 627 | text-decoration: none; 628 | } 629 | .btn-hg { 630 | padding: 13px 20px; 631 | font-size: 22px; 632 | line-height: 1.227; 633 | border-radius: 6px; 634 | } 635 | .btn-lg { 636 | padding: 10px 19px; 637 | font-size: 17px; 638 | line-height: 1.471; 639 | border-radius: 6px; 640 | } 641 | .btn-sm { 642 | padding: 9px 13px; 643 | font-size: 13px; 644 | line-height: 1.385; 645 | border-radius: 4px; 646 | } 647 | .btn-xs { 648 | padding: 6px 9px; 649 | font-size: 12px; 650 | line-height: 1.083; 651 | border-radius: 3px; 652 | } 653 | .btn-tip { 654 | font-weight: 300; 655 | padding-left: 10px; 656 | font-size: 92%; 657 | } 658 | .btn-block { 659 | white-space: normal; 660 | } 661 | .caret { 662 | border-width: 8px 6px; 663 | border-bottom-color: #34495e; 664 | border-top-color: #34495e; 665 | border-style: solid; 666 | border-bottom-style: none; 667 | -webkit-transition: 0.25s; 668 | transition: 0.25s; 669 | -webkit-transform: scale(1.001, ); 670 | -ms-transform: scale(1.001, ); 671 | transform: scale(1.001, ); 672 | } 673 | .dropup .caret, 674 | .dropup .btn-lg .caret, 675 | .navbar-fixed-bottom .dropdown .caret { 676 | border-bottom-width: 8px; 677 | } 678 | .btn-lg .caret { 679 | border-top-width: 8px; 680 | border-right-width: 6px; 681 | border-left-width: 6px; 682 | } 683 | .select { 684 | display: inline-block; 685 | margin-bottom: 10px; 686 | } 687 | [class*="span"] > .select[class*="span"] { 688 | margin-left: 0; 689 | } 690 | .select[class*="span"] .btn { 691 | width: 100%; 692 | } 693 | .select.select-block { 694 | display: block; 695 | float: none; 696 | margin-left: 0; 697 | width: auto; 698 | } 699 | .select.select-block:before, 700 | .select.select-block:after { 701 | content: " "; 702 | /* 1 */ 703 | display: table; 704 | /* 2 */ 705 | } 706 | .select.select-block:after { 707 | clear: both; 708 | } 709 | .select.select-block .btn { 710 | width: 100%; 711 | } 712 | .select.select-block .dropdown-menu { 713 | width: 100%; 714 | } 715 | .select .btn { 716 | width: 220px; 717 | } 718 | .select .btn.btn-hg .filter-option { 719 | left: 20px; 720 | right: 40px; 721 | top: 13px; 722 | } 723 | .select .btn.btn-hg .caret { 724 | right: 20px; 725 | } 726 | .select .btn.btn-lg .filter-option { 727 | left: 18px; 728 | right: 38px; 729 | } 730 | .select .btn.btn-sm .filter-option { 731 | left: 13px; 732 | right: 33px; 733 | } 734 | .select .btn.btn-sm .caret { 735 | right: 13px; 736 | } 737 | .select .btn.btn-xs .filter-option { 738 | left: 13px; 739 | right: 33px; 740 | top: 5px; 741 | } 742 | .select .btn.btn-xs .caret { 743 | right: 13px; 744 | } 745 | .select .btn .filter-option { 746 | height: 26px; 747 | left: 13px; 748 | overflow: hidden; 749 | position: absolute; 750 | right: 33px; 751 | text-align: left; 752 | top: 10px; 753 | } 754 | .select .btn .caret { 755 | position: absolute; 756 | right: 16px; 757 | top: 50%; 758 | margin-top: -3px; 759 | } 760 | .select .btn .dropdown-toggle { 761 | border-radius: 6px; 762 | } 763 | .select .btn .dropdown-menu { 764 | min-width: 100%; 765 | } 766 | .select .btn .dropdown-menu dt { 767 | cursor: default; 768 | display: block; 769 | padding: 3px 20px; 770 | } 771 | .select .btn .dropdown-menu li:not(.disabled) > a:hover small { 772 | color: rgba(255, 255, 255, 0.004); 773 | } 774 | .select .btn .dropdown-menu li > a { 775 | min-height: 20px; 776 | } 777 | .select .btn .dropdown-menu li > a.opt { 778 | padding-left: 35px; 779 | } 780 | .select .btn .dropdown-menu li small { 781 | padding-left: .5em; 782 | } 783 | .select .btn .dropdown-menu li > dt small { 784 | font-weight: normal; 785 | } 786 | .select .btn > .disabled, 787 | .select .btn .dropdown-menu li.disabled > a { 788 | cursor: default; 789 | } 790 | .select .caret { 791 | border-bottom-color: #ffffff; 792 | border-top-color: #ffffff; 793 | } 794 | 795 | textarea { 796 | font-size: 20px; 797 | line-height: 24px; 798 | padding: 5px 11px; 799 | } 800 | input[type="search"] { 801 | -webkit-appearance: none !important; 802 | } 803 | label { 804 | font-weight: normal; 805 | font-size: 15px; 806 | line-height: 2.4; 807 | } 808 | .form-control:-moz-placeholder { 809 | color: #b2bcc5; 810 | } 811 | .form-control::-moz-placeholder { 812 | color: #b2bcc5; 813 | opacity: 1; 814 | } 815 | .form-control:-ms-input-placeholder { 816 | color: #b2bcc5; 817 | } 818 | .form-control::-webkit-input-placeholder { 819 | color: #b2bcc5; 820 | } 821 | .form-control.placeholder { 822 | color: #b2bcc5; 823 | } 824 | .form-control { 825 | border: 2px solid #bdc3c7; 826 | color: #34495e; 827 | font-family: "Lato", Helvetica, Arial, sans-serif; 828 | font-size: 15px; 829 | line-height: 1.467; 830 | padding: 8px 12px; 831 | height: 42px; 832 | -webkit-appearance: none; 833 | border-radius: 6px; 834 | -webkit-box-shadow: none; 835 | box-shadow: none; 836 | -webkit-transition: border .25s linear, color .25s linear, background-color .25s linear; 837 | transition: border .25s linear, color .25s linear, background-color .25s linear; 838 | } 839 | .form-group.focus .form-control, 840 | .form-control:focus { 841 | border-color: #428bca; 842 | outline: 0; 843 | -webkit-box-shadow: none; 844 | box-shadow: none; 845 | } 846 | .form-control[disabled], 847 | .form-control[readonly], 848 | fieldset[disabled] .form-control { 849 | background-color: #f4f6f6; 850 | border-color: #d5dbdb; 851 | color: #d5dbdb; 852 | cursor: default; 853 | opacity: 0.7; 854 | filter: alpha(opacity=70); 855 | } 856 | .form-control.flat { 857 | border-color: transparent; 858 | } 859 | .form-control.flat:hover { 860 | border-color: #bdc3c7; 861 | } 862 | .form-control.flat:focus { 863 | border-color: #428bca; 864 | } 865 | .input-sm { 866 | height: 35px; 867 | padding: 6px 10px; 868 | font-size: 13px; 869 | line-height: 1.462; 870 | border-radius: 6px; 871 | } 872 | select.input-sm { 873 | height: 35px; 874 | line-height: 35px; 875 | } 876 | textarea.input-sm, 877 | select[multiple].input-sm { 878 | height: auto; 879 | } 880 | .input-lg { 881 | height: 45px; 882 | padding: 10px 15px; 883 | font-size: 17px; 884 | line-height: 1.235; 885 | border-radius: 6px; 886 | } 887 | select.input-lg { 888 | height: 45px; 889 | line-height: 45px; 890 | } 891 | textarea.input-lg, 892 | select[multiple].input-lg { 893 | height: auto; 894 | } 895 | .input-hg { 896 | height: 53px; 897 | padding: 10px 16px; 898 | font-size: 22px; 899 | line-height: 1.318; 900 | border-radius: 6px; 901 | } 902 | select.input-hg { 903 | height: 53px; 904 | line-height: 53px; 905 | } 906 | textarea.input-hg, 907 | select[multiple].input-hg { 908 | height: auto; 909 | } 910 | .has-warning .help-block, 911 | .has-warning .control-label, 912 | .has-warning .radio, 913 | .has-warning .checkbox, 914 | .has-warning .radio-inline, 915 | .has-warning .checkbox-inline { 916 | color: #f1c40f; 917 | } 918 | .has-warning .form-control { 919 | color: #f1c40f; 920 | border-color: #f1c40f; 921 | -webkit-box-shadow: none; 922 | box-shadow: none; 923 | } 924 | .has-warning .form-control:-moz-placeholder { 925 | color: #f1c40f; 926 | } 927 | .has-warning .form-control::-moz-placeholder { 928 | color: #f1c40f; 929 | opacity: 1; 930 | } 931 | .has-warning .form-control:-ms-input-placeholder { 932 | color: #f1c40f; 933 | } 934 | .has-warning .form-control::-webkit-input-placeholder { 935 | color: #f1c40f; 936 | } 937 | .has-warning .form-control.placeholder { 938 | color: #f1c40f; 939 | } 940 | .has-warning .form-control:focus { 941 | border-color: #f1c40f; 942 | -webkit-box-shadow: none; 943 | box-shadow: none; 944 | } 945 | .has-warning .input-group-addon { 946 | color: #f1c40f; 947 | border-color: #f1c40f; 948 | background-color: #ffffff; 949 | } 950 | .has-error .help-block, 951 | .has-error .control-label, 952 | .has-error .radio, 953 | .has-error .checkbox, 954 | .has-error .radio-inline, 955 | .has-error .checkbox-inline { 956 | color: #e74c3c; 957 | } 958 | .has-error .form-control { 959 | color: #e74c3c; 960 | border-color: #e74c3c; 961 | -webkit-box-shadow: none; 962 | box-shadow: none; 963 | } 964 | .has-error .form-control:-moz-placeholder { 965 | color: #e74c3c; 966 | } 967 | .has-error .form-control::-moz-placeholder { 968 | color: #e74c3c; 969 | opacity: 1; 970 | } 971 | .has-error .form-control:-ms-input-placeholder { 972 | color: #e74c3c; 973 | } 974 | .has-error .form-control::-webkit-input-placeholder { 975 | color: #e74c3c; 976 | } 977 | .has-error .form-control.placeholder { 978 | color: #e74c3c; 979 | } 980 | .has-error .form-control:focus { 981 | border-color: #e74c3c; 982 | -webkit-box-shadow: none; 983 | box-shadow: none; 984 | } 985 | .has-error .input-group-addon { 986 | color: #e74c3c; 987 | border-color: #e74c3c; 988 | background-color: #ffffff; 989 | } 990 | .has-success .help-block, 991 | .has-success .control-label, 992 | .has-success .radio, 993 | .has-success .checkbox, 994 | .has-success .radio-inline, 995 | .has-success .checkbox-inline { 996 | color: #2ecc71; 997 | } 998 | .has-success .form-control { 999 | color: #2ecc71; 1000 | border-color: #2ecc71; 1001 | -webkit-box-shadow: none; 1002 | box-shadow: none; 1003 | } 1004 | .has-success .form-control:-moz-placeholder { 1005 | color: #2ecc71; 1006 | } 1007 | .has-success .form-control::-moz-placeholder { 1008 | color: #2ecc71; 1009 | opacity: 1; 1010 | } 1011 | .has-success .form-control:-ms-input-placeholder { 1012 | color: #2ecc71; 1013 | } 1014 | .has-success .form-control::-webkit-input-placeholder { 1015 | color: #2ecc71; 1016 | } 1017 | .has-success .form-control.placeholder { 1018 | color: #2ecc71; 1019 | } 1020 | .has-success .form-control:focus { 1021 | border-color: #2ecc71; 1022 | -webkit-box-shadow: none; 1023 | box-shadow: none; 1024 | } 1025 | .has-success .input-group-addon { 1026 | color: #2ecc71; 1027 | border-color: #2ecc71; 1028 | background-color: #ffffff; 1029 | } 1030 | .help-block { 1031 | font-size: 15px; 1032 | margin-bottom: 5px; 1033 | color: inherit; 1034 | } 1035 | .form-group { 1036 | position: relative; 1037 | margin-bottom: 20px; 1038 | } 1039 | .form-horizontal .control-label, 1040 | .form-horizontal .radio, 1041 | .form-horizontal .checkbox, 1042 | .form-horizontal .radio-inline, 1043 | .form-horizontal .checkbox-inline { 1044 | margin-top: 0; 1045 | margin-bottom: 0; 1046 | padding-top: 6px; 1047 | } 1048 | .form-horizontal .form-group { 1049 | margin-left: -15px; 1050 | margin-right: -15px; 1051 | } 1052 | .form-horizontal .form-group:before, 1053 | .form-horizontal .form-group:after { 1054 | content: " "; 1055 | /* 1 */ 1056 | display: table; 1057 | /* 2 */ 1058 | } 1059 | .form-horizontal .form-group:after { 1060 | clear: both; 1061 | } 1062 | .form-horizontal .form-control-static { 1063 | padding-top: 6px; 1064 | } 1065 | .form-group { 1066 | position: relative; 1067 | } 1068 | .form-control + .input-icon { 1069 | position: absolute; 1070 | top: 2px; 1071 | right: 2px; 1072 | line-height: 37px; 1073 | vertical-align: middle; 1074 | font-size: 20px; 1075 | color: #b2bcc5; 1076 | background-color: #ffffff; 1077 | padding: 0 12px 0 0; 1078 | border-radius: 6px; 1079 | } 1080 | .input-hg + .input-icon { 1081 | line-height: 49px; 1082 | padding: 0 16px 0 0; 1083 | } 1084 | .input-lg + .input-icon { 1085 | line-height: 41px; 1086 | padding: 0 15px 0 0; 1087 | } 1088 | .input-sm + .input-icon { 1089 | font-size: 18px; 1090 | line-height: 30px; 1091 | padding: 0 10px 0 0; 1092 | } 1093 | .has-success .input-icon { 1094 | color: #2ecc71; 1095 | } 1096 | .has-warning .input-icon { 1097 | color: #f1c40f; 1098 | } 1099 | .has-error .input-icon { 1100 | color: #e74c3c; 1101 | } 1102 | .form-control[disabled] + .input-icon, 1103 | .form-control[readonly] + .input-icon, 1104 | fieldset[disabled] .form-control + .input-icon, 1105 | .form-control.disabled + .input-icon { 1106 | color: #d5dbdb; 1107 | background-color: transparent; 1108 | opacity: 0.7; 1109 | filter: alpha(opacity=70); 1110 | } 1111 | .input-group-hg > .form-control, 1112 | .input-group-hg > .input-group-addon, 1113 | .input-group-hg > .input-group-btn > .btn { 1114 | height: 53px; 1115 | padding: 10px 16px; 1116 | font-size: 22px; 1117 | line-height: 1.318; 1118 | border-radius: 6px; 1119 | } 1120 | select.input-group-hg > .form-control, 1121 | select.input-group-hg > .input-group-addon, 1122 | select.input-group-hg > .input-group-btn > .btn { 1123 | height: 53px; 1124 | line-height: 53px; 1125 | } 1126 | textarea.input-group-hg > .form-control, 1127 | textarea.input-group-hg > .input-group-addon, 1128 | textarea.input-group-hg > .input-group-btn > .btn, 1129 | select[multiple].input-group-hg > .form-control, 1130 | select[multiple].input-group-hg > .input-group-addon, 1131 | select[multiple].input-group-hg > .input-group-btn > .btn { 1132 | height: auto; 1133 | } 1134 | .input-group-lg > .form-control, 1135 | .input-group-lg > .input-group-addon, 1136 | .input-group-lg > .input-group-btn > .btn { 1137 | height: 45px; 1138 | padding: 10px 15px; 1139 | font-size: 17px; 1140 | line-height: 1.235; 1141 | border-radius: 6px; 1142 | } 1143 | select.input-group-lg > .form-control, 1144 | select.input-group-lg > .input-group-addon, 1145 | select.input-group-lg > .input-group-btn > .btn { 1146 | height: 45px; 1147 | line-height: 45px; 1148 | } 1149 | textarea.input-group-lg > .form-control, 1150 | textarea.input-group-lg > .input-group-addon, 1151 | textarea.input-group-lg > .input-group-btn > .btn, 1152 | select[multiple].input-group-lg > .form-control, 1153 | select[multiple].input-group-lg > .input-group-addon, 1154 | select[multiple].input-group-lg > .input-group-btn > .btn { 1155 | height: auto; 1156 | } 1157 | .input-group-sm > .form-control, 1158 | .input-group-sm > .input-group-addon, 1159 | .input-group-sm > .input-group-btn > .btn { 1160 | height: 35px; 1161 | padding: 6px 10px; 1162 | font-size: 13px; 1163 | line-height: 1.462; 1164 | border-radius: 6px; 1165 | } 1166 | select.input-group-sm > .form-control, 1167 | select.input-group-sm > .input-group-addon, 1168 | select.input-group-sm > .input-group-btn > .btn { 1169 | height: 35px; 1170 | line-height: 35px; 1171 | } 1172 | textarea.input-group-sm > .form-control, 1173 | textarea.input-group-sm > .input-group-addon, 1174 | textarea.input-group-sm > .input-group-btn > .btn, 1175 | select[multiple].input-group-sm > .form-control, 1176 | select[multiple].input-group-sm > .input-group-addon, 1177 | select[multiple].input-group-sm > .input-group-btn > .btn { 1178 | height: auto; 1179 | } 1180 | .input-group-addon { 1181 | padding: 10px 12px; 1182 | font-size: 15px; 1183 | color: #ffffff; 1184 | text-align: center; 1185 | background-color: #bdc3c7; 1186 | border: 1px solid #bdc3c7; 1187 | border-radius: 6px; 1188 | -webkit-transition: border .25s linear, color .25s linear, background-color .25s linear; 1189 | transition: border .25s linear, color .25s linear, background-color .25s linear; 1190 | } 1191 | .input-group-hg .input-group-addon, 1192 | .input-group-lg .input-group-addon, 1193 | .input-group-sm .input-group-addon { 1194 | line-height: 1; 1195 | } 1196 | .input-group .form-control:first-child, 1197 | .input-group-addon:first-child, 1198 | .input-group-btn:first-child > .btn, 1199 | .input-group-btn:first-child > .dropdown-toggle, 1200 | .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { 1201 | border-bottom-right-radius: 0; 1202 | border-top-right-radius: 0; 1203 | } 1204 | .input-group .form-control:last-child, 1205 | .input-group-addon:last-child, 1206 | .input-group-btn:last-child > .btn, 1207 | .input-group-btn:last-child > .dropdown-toggle, 1208 | .input-group-btn:first-child > .btn:not(:first-child) { 1209 | border-bottom-left-radius: 0; 1210 | border-top-left-radius: 0; 1211 | } 1212 | .form-group.focus .input-group-addon, 1213 | .input-group.focus .input-group-addon { 1214 | background-color: #428bca; 1215 | border-color: #428bca; 1216 | } 1217 | .form-group.focus .input-group-btn > .btn-default + .btn-default, 1218 | .input-group.focus .input-group-btn > .btn-default + .btn-default { 1219 | border-left-color: #428bca; 1220 | } 1221 | .form-group.focus .input-group-btn .btn, 1222 | .input-group.focus .input-group-btn .btn { 1223 | border-color: #428bca; 1224 | background-color: #ffffff; 1225 | color: #428bca; 1226 | } 1227 | .form-group.focus .input-group-btn .btn-default, 1228 | .input-group.focus .input-group-btn .btn-default { 1229 | color: #ffffff; 1230 | background-color: #428bca; 1231 | } 1232 | .form-group.focus .input-group-btn .btn-default:hover, 1233 | .input-group.focus .input-group-btn .btn-default:hover, 1234 | .form-group.focus .input-group-btn .btn-default:focus, 1235 | .input-group.focus .input-group-btn .btn-default:focus, 1236 | .form-group.focus .input-group-btn .btn-default:active, 1237 | .input-group.focus .input-group-btn .btn-default:active, 1238 | .form-group.focus .input-group-btn .btn-default.active, 1239 | .input-group.focus .input-group-btn .btn-default.active, 1240 | .open .dropdown-toggle.form-group.focus .input-group-btn .btn-default, 1241 | .open .dropdown-toggle.input-group.focus .input-group-btn .btn-default { 1242 | color: #ffffff; 1243 | background-color: #3276b1; 1244 | border-color: #3276b1; 1245 | } 1246 | .form-group.focus .input-group-btn .btn-default:active, 1247 | .input-group.focus .input-group-btn .btn-default:active, 1248 | .form-group.focus .input-group-btn .btn-default.active, 1249 | .input-group.focus .input-group-btn .btn-default.active, 1250 | .open .dropdown-toggle.form-group.focus .input-group-btn .btn-default, 1251 | .open .dropdown-toggle.input-group.focus .input-group-btn .btn-default { 1252 | background: #428bca; 1253 | border-color: #428bca; 1254 | } 1255 | .form-group.focus .input-group-btn .btn-default.disabled, 1256 | .input-group.focus .input-group-btn .btn-default.disabled, 1257 | .form-group.focus .input-group-btn .btn-default[disabled], 1258 | .input-group.focus .input-group-btn .btn-default[disabled], 1259 | fieldset[disabled] .form-group.focus .input-group-btn .btn-default, 1260 | fieldset[disabled] .input-group.focus .input-group-btn .btn-default, 1261 | .form-group.focus .input-group-btn .btn-default.disabled:hover, 1262 | .input-group.focus .input-group-btn .btn-default.disabled:hover, 1263 | .form-group.focus .input-group-btn .btn-default[disabled]:hover, 1264 | .input-group.focus .input-group-btn .btn-default[disabled]:hover, 1265 | fieldset[disabled] .form-group.focus .input-group-btn .btn-default:hover, 1266 | fieldset[disabled] .input-group.focus .input-group-btn .btn-default:hover, 1267 | .form-group.focus .input-group-btn .btn-default.disabled:focus, 1268 | .input-group.focus .input-group-btn .btn-default.disabled:focus, 1269 | .form-group.focus .input-group-btn .btn-default[disabled]:focus, 1270 | .input-group.focus .input-group-btn .btn-default[disabled]:focus, 1271 | fieldset[disabled] .form-group.focus .input-group-btn .btn-default:focus, 1272 | fieldset[disabled] .input-group.focus .input-group-btn .btn-default:focus, 1273 | .form-group.focus .input-group-btn .btn-default.disabled:active, 1274 | .input-group.focus .input-group-btn .btn-default.disabled:active, 1275 | .form-group.focus .input-group-btn .btn-default[disabled]:active, 1276 | .input-group.focus .input-group-btn .btn-default[disabled]:active, 1277 | fieldset[disabled] .form-group.focus .input-group-btn .btn-default:active, 1278 | fieldset[disabled] .input-group.focus .input-group-btn .btn-default:active, 1279 | .form-group.focus .input-group-btn .btn-default.disabled.active, 1280 | .input-group.focus .input-group-btn .btn-default.disabled.active, 1281 | .form-group.focus .input-group-btn .btn-default[disabled].active, 1282 | .input-group.focus .input-group-btn .btn-default[disabled].active, 1283 | fieldset[disabled] .form-group.focus .input-group-btn .btn-default.active, 1284 | fieldset[disabled] .input-group.focus .input-group-btn .btn-default.active { 1285 | background-color: #428bca; 1286 | border-color: #428bca; 1287 | } 1288 | .input-group-btn .btn { 1289 | background-color: #ffffff; 1290 | border: 2px solid #bdc3c7; 1291 | color: #bdc3c7; 1292 | line-height: 18px; 1293 | } 1294 | .input-group-btn .btn-default { 1295 | color: #ffffff; 1296 | background-color: #bdc3c7; 1297 | } 1298 | .input-group-btn .btn-default:hover, 1299 | .input-group-btn .btn-default:focus, 1300 | .input-group-btn .btn-default:active, 1301 | .input-group-btn .btn-default.active, 1302 | .open .dropdown-toggle.input-group-btn .btn-default { 1303 | color: #ffffff; 1304 | background-color: #cacfd2; 1305 | border-color: #cacfd2; 1306 | } 1307 | .input-group-btn .btn-default:active, 1308 | .input-group-btn .btn-default.active, 1309 | .open .dropdown-toggle.input-group-btn .btn-default { 1310 | background: #a1a6a9; 1311 | border-color: #a1a6a9; 1312 | } 1313 | .input-group-btn .btn-default.disabled, 1314 | .input-group-btn .btn-default[disabled], 1315 | fieldset[disabled] .input-group-btn .btn-default, 1316 | .input-group-btn .btn-default.disabled:hover, 1317 | .input-group-btn .btn-default[disabled]:hover, 1318 | fieldset[disabled] .input-group-btn .btn-default:hover, 1319 | .input-group-btn .btn-default.disabled:focus, 1320 | .input-group-btn .btn-default[disabled]:focus, 1321 | fieldset[disabled] .input-group-btn .btn-default:focus, 1322 | .input-group-btn .btn-default.disabled:active, 1323 | .input-group-btn .btn-default[disabled]:active, 1324 | fieldset[disabled] .input-group-btn .btn-default:active, 1325 | .input-group-btn .btn-default.disabled.active, 1326 | .input-group-btn .btn-default[disabled].active, 1327 | fieldset[disabled] .input-group-btn .btn-default.active { 1328 | background-color: #bdc3c7; 1329 | border-color: #bdc3c7; 1330 | } 1331 | .input-group-hg .input-group-btn .btn { 1332 | line-height: 31px; 1333 | } 1334 | .input-group-lg .input-group-btn .btn { 1335 | line-height: 21px; 1336 | } 1337 | .input-group-sm .input-group-btn .btn { 1338 | line-height: 19px; 1339 | } 1340 | .input-group-btn:first-child > .btn { 1341 | border-right-width: 0; 1342 | margin-right: -2px; 1343 | } 1344 | .input-group-btn:last-child > .btn { 1345 | border-left-width: 0; 1346 | margin-left: -2px; 1347 | } 1348 | .input-group-btn > .btn-default + .btn-default { 1349 | border-left: 2px solid #bdc3c7; 1350 | } 1351 | .input-group-btn > .btn:first-child + .btn .caret { 1352 | margin-left: 0; 1353 | } 1354 | .input-group-rounded .input-group-btn + .form-control, 1355 | .input-group-rounded .input-group-btn:last-child .btn { 1356 | border-bottom-right-radius: 20px; 1357 | border-top-right-radius: 20px; 1358 | } 1359 | .input-group-hg.input-group-rounded .input-group-btn + .form-control, 1360 | .input-group-hg.input-group-rounded .input-group-btn:last-child .btn { 1361 | border-bottom-right-radius: 27px; 1362 | border-top-right-radius: 27px; 1363 | } 1364 | .input-group-lg.input-group-rounded .input-group-btn + .form-control, 1365 | .input-group-lg.input-group-rounded .input-group-btn:last-child .btn { 1366 | border-bottom-right-radius: 25px; 1367 | border-top-right-radius: 25px; 1368 | } 1369 | .input-group-rounded .form-control:first-child, 1370 | .input-group-rounded .input-group-btn:first-child .btn { 1371 | border-bottom-left-radius: 20px; 1372 | border-top-left-radius: 20px; 1373 | } 1374 | .input-group-hg.input-group-rounded .form-control:first-child, 1375 | .input-group-hg.input-group-rounded .input-group-btn:first-child .btn { 1376 | border-bottom-left-radius: 27px; 1377 | border-top-left-radius: 27px; 1378 | } 1379 | .input-group-lg.input-group-rounded .form-control:first-child, 1380 | .input-group-lg.input-group-rounded .input-group-btn:first-child .btn { 1381 | border-bottom-left-radius: 25px; 1382 | border-top-left-radius: 25px; 1383 | } 1384 | .input-group-rounded .input-group-btn + .form-control { 1385 | padding-left: 0; 1386 | } 1387 | .checkbox, 1388 | .radio { 1389 | margin-bottom: 12px; 1390 | padding-left: 32px; 1391 | position: relative; 1392 | -webkit-transition: color 0.25s linear; 1393 | transition: color 0.25s linear; 1394 | font-size: 14px; 1395 | line-height: 1.5; 1396 | } 1397 | .checkbox input, 1398 | .radio input { 1399 | outline: none !important; 1400 | display: none; 1401 | } 1402 | .checkbox .icons, 1403 | .radio .icons { 1404 | color: #bdc3c7; 1405 | display: block; 1406 | height: 20px; 1407 | left: 0; 1408 | position: absolute; 1409 | top: 0; 1410 | width: 20px; 1411 | text-align: center; 1412 | line-height: 21px; 1413 | font-size: 20px; 1414 | cursor: pointer; 1415 | -webkit-transition: color 0.25s linear; 1416 | transition: color 0.25s linear; 1417 | } 1418 | .checkbox .icons .first-icon, 1419 | .radio .icons .first-icon, 1420 | .checkbox .icons .second-icon, 1421 | .radio .icons .second-icon { 1422 | display: inline-table; 1423 | position: absolute; 1424 | left: 0; 1425 | top: 0; 1426 | background-color: transparent; 1427 | margin: 0; 1428 | opacity: 1; 1429 | filter: alpha(opacity=100); 1430 | } 1431 | .checkbox .icons .second-icon, 1432 | .radio .icons .second-icon { 1433 | opacity: 0; 1434 | filter: alpha(opacity=0); 1435 | } 1436 | .checkbox:hover, 1437 | .radio:hover { 1438 | -webkit-transition: color 0.25s linear; 1439 | transition: color 0.25s linear; 1440 | } 1441 | .checkbox:hover .first-icon, 1442 | .radio:hover .first-icon { 1443 | opacity: 0; 1444 | filter: alpha(opacity=0); 1445 | } 1446 | .checkbox:hover .second-icon, 1447 | .radio:hover .second-icon { 1448 | opacity: 1; 1449 | filter: alpha(opacity=100); 1450 | } 1451 | .checkbox.checked, 1452 | .radio.checked { 1453 | color: #428bca; 1454 | } 1455 | .checkbox.checked .first-icon, 1456 | .radio.checked .first-icon { 1457 | opacity: 0; 1458 | filter: alpha(opacity=0); 1459 | } 1460 | .checkbox.checked .second-icon, 1461 | .radio.checked .second-icon { 1462 | opacity: 1; 1463 | filter: alpha(opacity=100); 1464 | color: #428bca; 1465 | -webkit-transition: color 0.25s linear; 1466 | transition: color 0.25s linear; 1467 | } 1468 | .checkbox.disabled, 1469 | .radio.disabled { 1470 | cursor: default; 1471 | color: #e6e8ea; 1472 | } 1473 | .checkbox.disabled .icons, 1474 | .radio.disabled .icons { 1475 | color: #e6e8ea; 1476 | } 1477 | .checkbox.disabled .first-icon, 1478 | .radio.disabled .first-icon { 1479 | opacity: 1; 1480 | filter: alpha(opacity=100); 1481 | } 1482 | .checkbox.disabled .second-icon, 1483 | .radio.disabled .second-icon { 1484 | opacity: 0; 1485 | filter: alpha(opacity=0); 1486 | } 1487 | .checkbox.disabled.checked .icons, 1488 | .radio.disabled.checked .icons { 1489 | color: #e6e8ea; 1490 | } 1491 | .checkbox.disabled.checked .first-icon, 1492 | .radio.disabled.checked .first-icon { 1493 | opacity: 0; 1494 | filter: alpha(opacity=0); 1495 | } 1496 | .checkbox.disabled.checked .second-icon, 1497 | .radio.disabled.checked .second-icon { 1498 | opacity: 1; 1499 | filter: alpha(opacity=100); 1500 | color: #e6e8ea; 1501 | } 1502 | .checkbox.primary .icons, 1503 | .radio.primary .icons { 1504 | color: #34495e; 1505 | } 1506 | .checkbox.primary.checked, 1507 | .radio.primary.checked { 1508 | color: #428bca; 1509 | } 1510 | .checkbox.primary.checked .icons, 1511 | .radio.primary.checked .icons { 1512 | color: #428bca; 1513 | } 1514 | .checkbox.primary.disabled, 1515 | .radio.primary.disabled { 1516 | cursor: default; 1517 | color: #bdc3c7; 1518 | } 1519 | .checkbox.primary.disabled .icons, 1520 | .radio.primary.disabled .icons { 1521 | color: #bdc3c7; 1522 | } 1523 | .checkbox.primary.disabled.checked .icons, 1524 | .radio.primary.disabled.checked .icons { 1525 | color: #bdc3c7; 1526 | } 1527 | .radio + .radio, 1528 | .checkbox + .checkbox { 1529 | margin-top: 10px; 1530 | } 1531 | .tooltip { 1532 | font-size: 14px; 1533 | line-height: 1.286; 1534 | } 1535 | .tooltip.in { 1536 | opacity: 1; 1537 | } 1538 | .tooltip.top { 1539 | padding-bottom: 9px; 1540 | } 1541 | .tooltip.top .tooltip-arrow { 1542 | border-top-color: #34495e; 1543 | border-width: 9px 9px 0; 1544 | bottom: 0; 1545 | margin-left: -9px; 1546 | } 1547 | .tooltip.right .tooltip-arrow { 1548 | border-right-color: #34495e; 1549 | border-width: 9px 9px 9px 0; 1550 | margin-top: -9px; 1551 | left: -3px; 1552 | } 1553 | .tooltip.bottom { 1554 | padding-top: 8px; 1555 | } 1556 | .tooltip.bottom .tooltip-arrow { 1557 | border-bottom-color: #34495e; 1558 | border-width: 0 9px 9px; 1559 | margin-left: -9px; 1560 | top: -1px; 1561 | } 1562 | .tooltip.left .tooltip-arrow { 1563 | border-left-color: #34495e; 1564 | border-width: 9px 0 9px 9px; 1565 | margin-top: -9px; 1566 | right: -3px; 1567 | } 1568 | .tooltip-inner { 1569 | background-color: #34495e; 1570 | line-height: 1.286; 1571 | padding: 12px 12px; 1572 | text-align: center; 1573 | width: 183px; 1574 | border-radius: 6px; 1575 | } 1576 | @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 1.5), only screen and (min-device-pixel-ratio: 2) { 1577 | .login { 1578 | background-image: url(../images/login/imac-2x.png); 1579 | } 1580 | } 1581 | -------------------------------------------------------------------------------- /res/css/style.css: -------------------------------------------------------------------------------- 1 | @CHARSET "UTF-8"; 2 | .clear { 3 | clear: both; 4 | } 5 | html,body{margin: 0;padding: 0} 6 | #nav { 7 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=90); 8 | opacity: 0.9; 9 | background: #FFF; 10 | padding: 0; 11 | margin:0; 12 | background: #446CB3; 13 | color:white; 14 | } 15 | #menu{ width: 80%; 16 | margin: 0 auto 17 | } 18 | #nav ul { 19 | margin: 0px auto; 20 | padding: 0px; 21 | list-style-type: none; 22 | } 23 | #nav ul li { 24 | float: left; 25 | padding: 10px 10px; 26 | font-size: 14px; 27 | border-right: 1px solid rgba(0, 0, 0, 0.1); 28 | box-shadow: 1px 0 0 rgba(255, 255, 255, 0.11); 29 | font-weight: bold; 30 | text-align: center; 31 | } 32 | .border_first{ 33 | border-left: 1px solid rgba(0, 0, 0, 0.1); 34 | box-shadow: 1px 0 0 rgba(255, 255, 255, 0.11); 35 | } 36 | 37 | #nav ul#left_submenu li:HOVER{ 38 | background: white; 39 | } 40 | #nav ul#left_submenu li:HOVER a{ 41 | color:#446CB3 42 | } 43 | #nav ul li a{display: inline-block;width:50px; color:white;} 44 | 45 | #network-tb td{ 46 | vertical-align: top 47 | } 48 | 49 | #aside { 50 | width:33%; 51 | min-width: 450px; 52 | } 53 | .w120{width: 120px} 54 | .w140{width: 140px} 55 | .w150{width: 150px} 56 | 57 | #connect_status{padding:6px;color: white} 58 | 59 | table {border-collapse: collapse;border-spacing: 0;} 60 | .tb_1{border:1px solid #cccccc;table-layout:fixed;word-break:break-all;width: 100%;background:#ffffff;margin-bottom:5px} 61 | .tb_1 caption{text-align: center;background: #F0F4F6;font-weight: bold;padding-top: 5px;height: 25px;border:1px solid #cccccc;border-bottom:none} 62 | .tb_1 a{margin:0 3px 0 3px} 63 | .tb_1 tr th,.tb_1 tr td{padding: 3px;border:1px solid #cccccc;line-height:20px} 64 | .tb_1 thead tr th{font-weight:bold;text-align: center;background:#e3eaee} 65 | .tb_1 tbody tr th{text-align: right;background:#f0f4f6;padding-right:5px} 66 | .tb_1 tfoot{color:#cccccc} 67 | .td_c td{text-align: center} 68 | .td_r td{text-align: right} 69 | .t_c{text-align: center !important;} 70 | .t_r{text-align: right !important;} 71 | .t_l{text-align: left !important;} 72 | 73 | .panel_1{background: #e5e2eb;padding:4px} 74 | #network_filter_form fieldset{padding-right:3px} 75 | 76 | #tb_network{font-size: 11px;margin-top: -8px;} 77 | #tb_network td{overflow: auto;cursor: pointer;} 78 | #right_content{margin-top:5px} 79 | #right_content table th,#right_content table td{font-size:11px;vertical-align: top} 80 | #right_content pre{border:none} 81 | 82 | #div_tb_network_list {overflow:auto;max-height: 1000px;min-height:450px} 83 | tr.selected{background: #ccffee;} 84 | .right{float: right} 85 | .left{float:left} 86 | .c{clear: both} 87 | .hide{display:none} 88 | #network_list_div tbody th{text-align: right;} 89 | .td_ul{list-style: none inside;padding: 0;margin: 0} 90 | .td_ul li{list-style: none inside;} 91 | 92 | td.td_has_sub{padding: 0 !important;} 93 | td.td_has_sub .tb_1{margin: 0;border: 0;} 94 | 95 | #bd0{margin:0 auto;width:80%} 96 | .form-control{height: 26px;padding:0} 97 | fieldset { 98 | border-radius:10px; 99 | border: 1px solid gray 100 | } 101 | tr.replay{ 102 | color:blue; 103 | } 104 | 105 | .res_td_body{ 106 | max-height: 120px; 107 | overflow: scroll; 108 | } 109 | 110 | .div_b_i{ 111 | display: inline-block; 112 | } 113 | 114 | .login-screen { 115 | min-height: 400px; 116 | width:300px; 117 | margin: 0 auto; 118 | text-align: center; 119 | } 120 | 121 | #bd{ 122 | margin: 0 auto; 123 | width:80%; 124 | padding-top: 30px; 125 | } 126 | 127 | .form_0 table{width: 100%} 128 | .form_0 th{text-align: right;width: 180px;vertical-align:top;} 129 | .form_0 input[type=text]{width: 100%;margin-bottom:2px} 130 | .form_0 td.last{width: 90px;padding-left: 10px} 131 | .oneline{height: 20px;overflow: hidden;} 132 | 133 | img {margin-bottom: -3px} -------------------------------------------------------------------------------- /res/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidu/pproxy/cc3978cfd1b726740b9d6925c7cd2a9cdf43c998/res/img/favicon.ico -------------------------------------------------------------------------------- /res/img/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidu/pproxy/cc3978cfd1b726740b9d6925c7cd2a9cdf43c998/res/img/folder.png -------------------------------------------------------------------------------- /res/img/list-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidu/pproxy/cc3978cfd1b726740b9d6925c7cd2a9cdf43c998/res/img/list-add.png -------------------------------------------------------------------------------- /res/img/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidu/pproxy/cc3978cfd1b726740b9d6925c7cd2a9cdf43c998/res/img/list.png -------------------------------------------------------------------------------- /res/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidu/pproxy/cc3978cfd1b726740b9d6925c7cd2a9cdf43c998/res/img/logo.png -------------------------------------------------------------------------------- /res/img/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidu/pproxy/cc3978cfd1b726740b9d6925c7cd2a9cdf43c998/res/img/text.png -------------------------------------------------------------------------------- /res/img/view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hidu/pproxy/cc3978cfd1b726740b9d6925c7cd2a9cdf43c998/res/img/view.png -------------------------------------------------------------------------------- /res/js/base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Base64 encode / decode 4 | * http://www.webtoolkit.info/ 5 | * 6 | **/ 7 | var Base64 = { 8 | 9 | // private property 10 | _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 11 | 12 | // public method for encoding 13 | encode : function (input) { 14 | var output = ""; 15 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 16 | var i = 0; 17 | 18 | input = Base64._utf8_encode(input); 19 | 20 | while (i < input.length) { 21 | 22 | chr1 = input.charCodeAt(i++); 23 | chr2 = input.charCodeAt(i++); 24 | chr3 = input.charCodeAt(i++); 25 | 26 | enc1 = chr1 >> 2; 27 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 28 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 29 | enc4 = chr3 & 63; 30 | 31 | if (isNaN(chr2)) { 32 | enc3 = enc4 = 64; 33 | } else if (isNaN(chr3)) { 34 | enc4 = 64; 35 | } 36 | 37 | output = output + 38 | this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + 39 | this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 40 | 41 | } 42 | 43 | return output; 44 | }, 45 | 46 | // public method for decoding 47 | decode : function (input) { 48 | var output = ""; 49 | var chr1, chr2, chr3; 50 | var enc1, enc2, enc3, enc4; 51 | var i = 0; 52 | 53 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 54 | 55 | while (i < input.length) { 56 | 57 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 58 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 59 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 60 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 61 | 62 | chr1 = (enc1 << 2) | (enc2 >> 4); 63 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 64 | chr3 = ((enc3 & 3) << 6) | enc4; 65 | 66 | output = output + String.fromCharCode(chr1); 67 | 68 | if (enc3 != 64) { 69 | output = output + String.fromCharCode(chr2); 70 | } 71 | if (enc4 != 64) { 72 | output = output + String.fromCharCode(chr3); 73 | } 74 | 75 | } 76 | 77 | output = Base64._utf8_decode(output); 78 | 79 | return output; 80 | 81 | }, 82 | 83 | // private method for UTF-8 encoding 84 | _utf8_encode : function (string) { 85 | string = string.replace(/\r\n/g,"\n"); 86 | var utftext = ""; 87 | 88 | for (var n = 0; n < string.length; n++) { 89 | 90 | var c = string.charCodeAt(n); 91 | 92 | if (c < 128) { 93 | utftext += String.fromCharCode(c); 94 | } 95 | else if((c > 127) && (c < 2048)) { 96 | utftext += String.fromCharCode((c >> 6) | 192); 97 | utftext += String.fromCharCode((c & 63) | 128); 98 | } 99 | else { 100 | utftext += String.fromCharCode((c >> 12) | 224); 101 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 102 | utftext += String.fromCharCode((c & 63) | 128); 103 | } 104 | 105 | } 106 | 107 | return utftext; 108 | }, 109 | 110 | // private method for UTF-8 decoding 111 | _utf8_decode : function (utftext) { 112 | var string = ""; 113 | var i = 0; 114 | var c = c1 = c2 = 0; 115 | 116 | while ( i < utftext.length ) { 117 | 118 | c = utftext.charCodeAt(i); 119 | 120 | if (c < 128) { 121 | string += String.fromCharCode(c); 122 | i++; 123 | } 124 | else if((c > 191) && (c < 224)) { 125 | c2 = utftext.charCodeAt(i+1); 126 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 127 | i += 2; 128 | } 129 | else { 130 | c2 = utftext.charCodeAt(i+1); 131 | c3 = utftext.charCodeAt(i+2); 132 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 133 | i += 3; 134 | } 135 | 136 | } 137 | 138 | return string; 139 | } 140 | 141 | } -------------------------------------------------------------------------------- /res/js/default.js: -------------------------------------------------------------------------------- 1 | function pproxy_tab_sup(target){ 2 | $(target).find("textarea").bind('keydown', function(e) { 3 | if (e.keyCode == 9 ) { 4 | e.preventDefault(); 5 | this.setRangeText('\t'); 6 | this.selectionEnd = ++this.selectionStart; 7 | } 8 | }); 9 | } -------------------------------------------------------------------------------- /res/js/session.js: -------------------------------------------------------------------------------- 1 | var socket = io.connect(); 2 | var connectNum=0; 3 | var pproxy_colors=["#FFFFFF","#CCFFFF","#FFCCCC","#99CCCC","996699","#CC9999","#0099CC","#FFFF66","#336633","#99CC00"] 4 | 5 | var ip_colors={} 6 | var pproxy_session_max_len=0; 7 | var pproxy_table_max_row=2000; 8 | 9 | 10 | var pproxy_req_list=[]; 11 | 12 | if(window.localStorage){ 13 | pproxy_req_list=$.parseJSON(window.localStorage["reqs"]||"[]") 14 | pproxy_session_max_len=1000; 15 | } 16 | 17 | function pproxy_show_reqs_from_local(){ 18 | for(var i=0;i
url | " + h(req["url"]) + " view |
---|---|
origin | " + h(req["url_origin"]) + " |
msg | " + h(req["msg"])+" |
proxy_urer | " + 199 | "remote_addr : " +req["client_ip"] + " docid : "+ req["id"] + 200 | " |
req_dump | " + h(Base64.decode(req["dump"])).replace(/\n/g, " ") 205 | + " |
msg | " + h(res["msg"])+" |
---|---|
res_dump | " + h(Base64.decode(res["dump"])).replace(/\n/g, " ") 224 | + " |
body_json | " + bd_json + " |
body_img | |
body"; 245 | if(res["body"].length>400){ 246 | html+=""; 247 | }else{ 248 | hideBigBody=false; 249 | } 250 | html+= " | " + 251 | "" +
252 | " " + h(body_str).replace(/\n/g, " ") + 253 | " |
" + json_str + ""; 295 | } 296 | } catch (e) { 297 | console.log("pproxy_parseAsjson_error",e); 298 | } 299 | return false; 300 | } 301 | 302 | function pproxy_tr_sub_table(obj, name) { 303 | if (!obj) { 304 | return ""; 305 | } 306 | var html = "
" + k + " |
|
---|
about
4 |HTTP protocol analysis tool.
5 |Based on BS architecture,written by golang.
6 |Project : https://github.com/hidu/pproxy
7 |Current Version: {{.version}}
8 | 9 |{%my_include "config/req_form.html" %} |
{%my_include "config/req_demo.html" %} |
{%my_include "config/hosts_form.html" %} |
{%my_include "config/hosts_demo.html" %} |
no | 13 |14 | {{if .isSubDir}} 15 | ../ 16 | {{end}} 17 | Name 18 | | 19 |Size | 20 |
---|---|---|
{{$k}} | 26 |
27 |
28 | {{if $file.IsDir}} ![]() ![]() ![]() ![]() |
38 | {{$file.Size}} | 39 |
8 |
9 |
60 |
61 |
10 |
37 |
38 |
39 |
40 |
47 | #
41 | host
42 |
43 | path
44 |
45 |
46 |
48 |
59 |
|
62 | 63 | 64 | | 65 |
Useage
4 |1.Client(eg:phone)
5 |6 | set wifi http proxy: 7 | proxy host : {{.pproxy_host}} 8 | proxy port : {{.pproxy_port}} 9 | 10 |11 | android users can use proxyDroid to manager proxys 12 | 13 |
2.Server:user interface
14 |15 | visit Session List Page to view all the http request through this proxy. 16 | in the session filter form, all text input can use| to enter multiple conditions. 17 | eg user:,it mean user is a or b. 18 | you can use replay to replay a request. 19 |20 | 21 |
3.Modify Requests
22 |
25 | function rewrite(req){
26 | //you code start
27 | if(req.host=="www.baidu.com"){
28 | req.host="www.163.com"
29 | req.host_addr="127.0.0.0:81" // send req to 127.0.0.1:81
30 | form_get.add("a","a")
31 | //form_post.set("d","a")
32 | }
33 |
34 | // you code end
35 | return req
36 | }
37 |
38 | 40 | schema : http 41 | host : www.example.com 42 | port : 80 43 | path : /album/list 44 | get: {cid:[123]} 45 | post: {} 46 | username : 47 | password : 48 | method: GET 49 | form_get : {add:function(k,v){},set:function(k,v){},get:function(k){},len:function(){}} 50 | form_post : {add:function(k,v){},set:function(k,v){},get:function(k){},len:function(){}} 51 | 52 | host_addr: #the real host you wish,eg 127.0.0.1:3218 53 |54 | 55 |
4.Modify Hosts
56 |57 | #all port 58 | news.baidu.com 127.0.0.1 59 | 60 | #only 81 port match 61 | news.baidu.com:81 127.0.0.1:82 62 | 63 | news.163.com 127.0.0.1:8080 64 |65 |