Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 114 lines (100 sloc) 3.521 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
exports = window.net = {}

# TCP socket.
# Events emitted:
# - 'connect': the connection succeeded, proceed.
# - 'data': data received. Argument is the data (array of longs, atm)
# - 'end': the other end sent a FIN packet, and won't accept any more data.
# - 'error': an error occurred. The socket is pretty much hosed now. (TODO:
# investigate how node deals with errors. The docs say 'close' gets sent right
# after 'error', so they probably destroy the socket.)
# - 'close': emitted when the socket is fully closed.
# TODO: 'drain': emitted when the write buffer becomes empty
class Socket
constructor: ->
@listeners = {}
on: (ev, cb) ->
(@listeners[ev] ?= []).push cb
removeListener: (ev, cb) ->
return unless @listeners and @listeners[ev] and cb?
@listeners[ev] = (l for l in @listeners[ev] when l != cb and l.listener != cb)
once: (ev, cb) ->
@on ev, f = (args...) =>
@removeListener ev, f
cb(args...)
f.listener = cb
emit: (ev, args...) ->
l(args...) for l in (@listeners[ev] ? [])

connect: (port, host='localhost') ->
@_active()
go = (err, addr) =>
return @emit 'error', "couldn't resolve: #{err}" if err
@_active()
chrome.experimental.socket.create 'tcp', {}, (si) =>
@socketId = si.socketId
if @socketId > 0
chrome.experimental.socket.connect @socketId, addr, port, @_onConnect
else
return @emit 'error', "couldn't create socket"

if /^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/.test host
go null, host
else
Socket.resolve host, go

_onConnect: (rc) =>
if rc < 0
# TODO: I'm pretty sure we should never get a -1 here..
# TODO: we should destroy the socket when we get an error.
@emit 'error', rc
else
@emit 'connect'
chrome.experimental.socket.read @socketId, @_onRead

_onRead: (readInfo) =>
console.error "Bad assumption: got -1 in _onRead" if readInfo.resultCode is -1
console.log readInfo
@_active()
if readInfo.resultCode < 0
@emit 'error', readInfo.resultCode
else if readInfo.resultCode is 0
@emit 'end'
@destroy() # TODO: half-open sockets
if readInfo.data.byteLength
@emit 'data', readInfo.data

chrome.experimental.socket.read @socketId, @_onRead

write: (data) ->
@_active()
chrome.experimental.socket.write @socketId, data, (writeInfo) =>
if writeInfo.resultCode < 0
console.error "SOCKET ERROR on write: ", writeInfo.resultCode
console.log "Wrote #{writeInfo.bytesWritten} of #{data.byteLength} bytes"
if writeInfo.bytesWritten == data.byteLength
@emit 'drain' # TODO not sure if this works, don't rely on this message
else
console.error "Waaah can't handle non-complete writes"

# looks to me like there's no equivalent to node's end() in the socket API
destroy: ->
chrome.experimental.socket.disconnect @socketId
@emit 'close' # TODO: figure out whether i should emit 'end' as well?

end: ->
# TODO: only half-close the socket
chrome.experimental.socket.disconnect @socketId
@emit 'close'

_active: ->
if @timeout
clearTimeout @timeout
@timeout = setTimeout (=> @emit 'timeout'), @timeout_ms

setTimeout: (ms, cb) ->
if ms > 0
@timeout = setTimeout (=> @emit 'timeout'), ms
@timeout_ms = ms
@once 'timeout', cb if cb
else if ms == 0
clearTimeout @timeout
@removeListener 'timeout', cb if cb
@timeout = null
@timeout_ms = 0

@resolve: (host, cb) ->
chrome.experimental.dns.resolve host, (res) ->
if res.resultCode is 0
cb(null, res.address)
else
cb(res.resultCode)

exports.Socket = Socket
Something went wrong with that request. Please try again.