Tizen 2.0 Release
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.2.0 / node_modules / testswarm / node_modules / request / tunnel.js
1 'use strict'
2
3 var net = require('net')
4   , tls = require('tls')
5   , http = require('http')
6   , https = require('https')
7   , events = require('events')
8   , assert = require('assert')
9   , util = require('util')
10   ;
11
12 exports.httpOverHttp = httpOverHttp
13 exports.httpsOverHttp = httpsOverHttp
14 exports.httpOverHttps = httpOverHttps
15 exports.httpsOverHttps = httpsOverHttps
16
17
18 function httpOverHttp(options) {
19   var agent = new TunnelingAgent(options)
20   agent.request = http.request
21   return agent
22 }
23
24 function httpsOverHttp(options) {
25   var agent = new TunnelingAgent(options)
26   agent.request = http.request
27   agent.createSocket = createSecureSocket
28   return agent
29 }
30
31 function httpOverHttps(options) {
32   var agent = new TunnelingAgent(options)
33   agent.request = https.request
34   return agent
35 }
36
37 function httpsOverHttps(options) {
38   var agent = new TunnelingAgent(options)
39   agent.request = https.request
40   agent.createSocket = createSecureSocket
41   return agent
42 }
43
44
45 function TunnelingAgent(options) {
46   var self = this
47   self.options = options || {}
48   self.proxyOptions = self.options.proxy || {}
49   self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets
50   self.requests = []
51   self.sockets = []
52
53   self.on('free', function onFree(socket, host, port) {
54     for (var i = 0, len = self.requests.length; i < len; ++i) {
55       var pending = self.requests[i]
56       if (pending.host === host && pending.port === port) {
57         // Detect the request to connect same origin server,
58         // reuse the connection.
59         self.requests.splice(i, 1)
60         pending.request.onSocket(socket)
61         return
62       }
63     }
64     socket.destroy()
65     self.removeSocket(socket)
66   })
67 }
68 util.inherits(TunnelingAgent, events.EventEmitter)
69
70 TunnelingAgent.prototype.addRequest = function addRequest(req, host, port) {
71   var self = this
72
73   if (self.sockets.length >= this.maxSockets) {
74     // We are over limit so we'll add it to the queue.
75     self.requests.push({host: host, port: port, request: req})
76     return
77   }
78
79   // If we are under maxSockets create a new one.
80   self.createSocket({host: host, port: port, request: req}, function(socket) {
81     socket.on('free', onFree)
82     socket.on('close', onCloseOrRemove)
83     socket.on('agentRemove', onCloseOrRemove)
84     req.onSocket(socket)
85
86     function onFree() {
87       self.emit('free', socket, host, port)
88     }
89
90     function onCloseOrRemove(err) {
91       self.removeSocket()
92       socket.removeListener('free', onFree)
93       socket.removeListener('close', onCloseOrRemove)
94       socket.removeListener('agentRemove', onCloseOrRemove)
95     }
96   })
97 }
98
99 TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
100   var self = this
101   var placeholder = {}
102   self.sockets.push(placeholder)
103
104   var connectOptions = mergeOptions({}, self.proxyOptions, 
105     { method: 'CONNECT'
106     , path: options.host + ':' + options.port
107     , agent: false
108     }
109   )
110   if (connectOptions.proxyAuth) {
111     connectOptions.headers = connectOptions.headers || {}
112     connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
113         new Buffer(connectOptions.proxyAuth).toString('base64')
114   }
115
116   debug('making CONNECT request')
117   var connectReq = self.request(connectOptions)
118   connectReq.useChunkedEncodingByDefault = false // for v0.6
119   connectReq.once('response', onResponse) // for v0.6
120   connectReq.once('upgrade', onUpgrade)   // for v0.6
121   connectReq.once('connect', onConnect)   // for v0.7 or later
122   connectReq.once('error', onError)
123   connectReq.end()
124
125   function onResponse(res) {
126     // Very hacky. This is necessary to avoid http-parser leaks.
127     res.upgrade = true
128   }
129
130   function onUpgrade(res, socket, head) {
131     // Hacky.
132     process.nextTick(function() {
133       onConnect(res, socket, head)
134     })
135   }
136
137   function onConnect(res, socket, head) {
138     connectReq.removeAllListeners()
139     socket.removeAllListeners()
140
141     if (res.statusCode === 200) {
142       assert.equal(head.length, 0)
143       debug('tunneling connection has established')
144       self.sockets[self.sockets.indexOf(placeholder)] = socket
145       cb(socket)
146     } else {
147       debug('tunneling socket could not be established, statusCode=%d', res.statusCode)
148       var error = new Error('tunneling socket could not be established, ' + 'statusCode=' + res.statusCode)
149       error.code = 'ECONNRESET'
150       options.request.emit('error', error)
151       self.removeSocket(placeholder)
152     }
153   }
154
155   function onError(cause) {
156     connectReq.removeAllListeners()
157
158     debug('tunneling socket could not be established, cause=%s\n', cause.message, cause.stack)
159     var error = new Error('tunneling socket could not be established, ' + 'cause=' + cause.message)
160     error.code = 'ECONNRESET'
161     options.request.emit('error', error)
162     self.removeSocket(placeholder)
163   }
164 }
165
166 TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {
167   var pos = this.sockets.indexOf(socket)
168   if (pos === -1) return
169   
170   this.sockets.splice(pos, 1)
171
172   var pending = this.requests.shift()
173   if (pending) {
174     // If we have pending requests and a socket gets closed a new one
175     // needs to be created to take over in the pool for the one that closed.
176     this.createSocket(pending, function(socket) {
177       pending.request.onSocket(socket)
178     })
179   }
180 }
181
182 function createSecureSocket(options, cb) {
183   var self = this
184   TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {
185     // 0 is dummy port for v0.6
186     var secureSocket = tls.connect(0, mergeOptions({}, self.options, 
187       { servername: options.host
188       , socket: socket
189       }
190     ))
191     cb(secureSocket)
192   })
193 }
194
195
196 function mergeOptions(target) {
197   for (var i = 1, len = arguments.length; i < len; ++i) {
198     var overrides = arguments[i]
199     if (typeof overrides === 'object') {
200       var keys = Object.keys(overrides)
201       for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {
202         var k = keys[j]
203         if (overrides[k] !== undefined) {
204           target[k] = overrides[k]
205         }
206       }
207     }
208   }
209   return target
210 }
211
212
213 var debug
214 if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
215   debug = function() {
216     var args = Array.prototype.slice.call(arguments)
217     if (typeof args[0] === 'string') {
218       args[0] = 'TUNNEL: ' + args[0]
219     } else {
220       args.unshift('TUNNEL:')
221     }
222     console.error.apply(console, args)
223   }
224 } else {
225   debug = function() {}
226 }
227 exports.debug = debug // for test