Skip to content

Instantly share code, notes, and snippets.

@kasuganosora
Created April 9, 2014 13:30
Show Gist options
  • Save kasuganosora/10270685 to your computer and use it in GitHub Desktop.
Save kasuganosora/10270685 to your computer and use it in GitHub Desktop.
#### 以下是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