Created
April 9, 2014 13:30
-
-
Save kasuganosora/10270685 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#### 以下是HTTP PROXY的实现 | |
net = require 'net' | |
localPort = 8011 | |
findBodyStartPos = (buffer)-> | |
length = buffer.length | |
for index in [0..buffer.length] | |
# http header end \r\n\r\n pos | |
return index+4 if buffer[index] == 0x0d and buffer[index+1] == 0x0a and buffer[index+2] == 0x0d and buffer[index+3] == 0x0a | |
return -1 # not found | |
parseHttpHeader = (str)-> | |
str = str.trim() | |
lines = str.split '\n' | |
return null if lines.length < 2 | |
header = {} | |
fistLine = lines.shift().trim() | |
try | |
fistLinePart = fistLine.split(' ') | |
header.method = fistLinePart[0] | |
if header.method is 'CONNECT' | |
# 当是要建立HTTP隧道的时候 | |
header.target = {} | |
[header.target.host,header.target.port] = fistLinePart[1].split(":") | |
else | |
header.pathname = fistLinePart[1] | |
[header.protocol,header.protocolVer] = fistLinePart[2].split('/') | |
for line in lines | |
separatorIndex = line.indexOf(":") | |
key = line.substr(0, separatorIndex).trim() | |
val = line.substr(separatorIndex + 1).trim() | |
if key is "Host" and header.method isnt 'CONNECT' | |
header.target = {} | |
port = 80 | |
sIndex = val.split(':') | |
if sIndex > -1 | |
header.target.host = val.substr(0,sIndex).trim() | |
header.target.port = val.substr(sIndex+1).trim() | |
else | |
header.target.host = val | |
header.target.port = 80 | |
else | |
header[key] = val | |
return header | |
catch e | |
return null | |
#从http请求头部取得请求信息后,继续监听浏览器发送数据,同时连接目标服务器,并把目标服务器的数据传给浏览器 | |
httpProxyRequest = (client,req,buffer)-> | |
console.log "connecting #{req.target.host}:#{req.target.port}" | |
# 如果请求不是CONNECT方法(GET, POST),那么替换掉头部的一些东西 | |
if req.method isnt 'CONNECT' | |
bodyPos = findBodyStartPos buffer | |
bodyPos = buffer.length if bodyPos < 0 | |
header = buffer.slice(0,bodyPos).toString('utf8') | |
header = header.replace(/(proxy\-)?connection\:.+\r\n/ig,'') | |
.replace(/Keep\-Alive\:.+\r\n/i,'') | |
if req.protocolVer is '1.1' | |
pathname = req.pathname.replace(/http\:\/\/[^\/]+/,'') | |
header = header.replace(req.pathname,pathname) if req.pathname isnt pathname | |
buffer = Buffer.concat [ new Buffer(header,'utf8'), buffer.slice(bodyPos) ] | |
# 建立到目标服务器的连接 | |
poxyServerConn = net.createConnection req.target.port, req.target.host | |
client.on "data",(data)-> | |
poxyServerConn.write data if poxyServerConn | |
client.on "end", -> | |
poxyServerConn.end() if poxyServerConn | |
client.on "error",(e)-> | |
console.log e | |
poxyServerConn.destroy() if poxyServerConn | |
client.on "drain", -> | |
poxyServerConn.resume() if poxyServerConn | |
poxyServerConn.on "data", (data)-> | |
client.write data | |
poxyServerConn.on 'end', -> | |
client.end() | |
poxyServerConn.on 'error', (e)-> | |
console.log "remote: #{req.target.host}:#{req.target.port} #{e}" | |
client.destroy() | |
poxyServerConn.on 'drain', -> | |
client.resume() | |
#返回http连接就绪的报文 | |
if req.method is "CONNECT" | |
client.write new Buffer("HTTP/1.1 200 Connection established\r\nConnection: close\r\n\r\n") | |
else | |
poxyServerConn.write buffer | |
httpProxyServiceHandle = (client)-> | |
buffer = new Buffer 0 | |
#首先监听浏览器的数据发送事件,直到收到的数据包含完整的http请求头 | |
client.on 'data', (data)-> | |
buffer = Buffer.concat [buffer,data] | |
return undefined if findBodyStartPos(buffer) is -1 | |
req = parseHttpHeader buffer.toString "utf8" | |
return undefined if req is null | |
client.removeAllListeners 'data' | |
httpProxyRequest client, req, buffer | |
# 建立本地监听服务 | |
net.createServer(httpProxyServiceHandle).listen localPort,()-> | |
console.log "HttpService Running" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment