root/HTTP/TCPServer.lua

Revision 1129 (checked in by rsz, 10 months ago)

cleanup

Line 
1 --------------------------------------------------------------------------------
2 -- Title:               TCPServer.lua
3 -- Description:         Like a square peg in a round hole
4 -- Author:              Raphaël Szwarc http://alt.textdrive.com/lua/
5 -- Creation Date:       January 30, 2007
6 -- Legal:               Copyright (C) 2007 Raphaël Szwarc
7 --                      Under the terms of the MIT License
8 --                      http://www.opensource.org/licenses/mit-license.html
9 --------------------------------------------------------------------------------
10
11 -- import dependencies
12 local debug = require( 'debug' )
13 local io = require( 'io' )
14 local os = require( 'os' )
15 local socket = require( 'socket' )
16 local table = require( 'table' )
17
18 local assert = assert
19 local getfenv = getfenv
20 local getmetatable = getmetatable
21 local select = select
22 local setfenv = setfenv
23 local setmetatable = setmetatable
24 local tostring = tostring
25 local unpack = unpack
26 local xpcall = xpcall
27
28 --------------------------------------------------------------------------------
29 -- Utilities
30 --------------------------------------------------------------------------------
31
32 local getenv = os.getenv
33
34 function os.getenv( aVariable )
35     local anEnvironment = assert( getfenv() )
36    
37     return getenv( aVariable ) or anEnvironment[ aVariable ]
38 end
39
40 --------------------------------------------------------------------------------
41 -- TCPServer
42 --------------------------------------------------------------------------------
43
44 module( 'TCPServer' )
45 _VERSION = '1.0'
46
47 local self = setmetatable( _M, {} )
48 local meta = getmetatable( self )
49
50 function Server( anAddress, aPort, aBacklog )
51     local aServer = assert( socket.tcp() )
52    
53     assert( aServer:setoption( 'keepalive', true ) )
54     assert( aServer:setoption( 'linger', { on = false, timeout = 0 } ) )
55     assert( aServer:setoption( 'reuseaddr', true ) )
56     assert( aServer:setoption( 'tcp-nodelay', true ) )
57     assert( aServer:settimeout( nil ) )
58     assert( aServer:bind( anAddress, aPort ) )
59     assert( aServer:listen( aBacklog ) )
60
61     return aServer
62 end
63
64 function ServerEnvironment( aServer )
65     local anEnvironment = { PROTO = 'TCP' }
66     local anAddress, aPort = assert( aServer:getsockname() )
67     local aHost = socket.dns.tohostname( anAddress )
68        
69     anEnvironment[ 'TCPLOCALIP' ] = anAddress
70     anEnvironment[ 'TCPLOCALPORT' ] = aPort
71     anEnvironment[ 'TCPLOCALHOST' ] = aHost
72    
73     return anEnvironment
74 end
75
76 function meta:__call( anAddress, aPort, aBacklog )
77     local aServer = Server( anAddress, aPort, aBacklog )
78     local anEnvironment = ServerEnvironment( aServer )
79     local aTCPServer = { server = aServer, environment =  anEnvironment }
80    
81     setmetatable( aTCPServer, self )
82     self.__index = self   
83    
84     return aTCPServer
85 end
86
87 function meta:__concat( aValue )
88     return tostring( self ) .. tostring( aValue )
89 end
90
91 function meta:__tostring()
92     return _NAME
93 end
94
95 local function ClientEnvironment( aClient, anEnvironment )
96     local anAddress, aPort = assert( aClient:getpeername() )
97     local aHost = socket.dns.tohostname( anAddress )
98
99     anEnvironment[ 'TCPREMOTEIP' ] = anAddress
100     anEnvironment[ 'TCPREMOTEPORT' ] = aPort
101     anEnvironment[ 'TCPREMOTEHOST' ] = aHost
102    
103     return anEnvironment
104 end
105
106 local function Call( aTCPServer, aHandler )
107     return function()
108         local aServer = aTCPServer.server
109         local aClient = assert( aServer:accept() )
110         local anEnvironment = ClientEnvironment( aClient, aTCPServer.environment )
111
112         aTCPServer.client = aClient
113         assert( aClient:setoption( 'linger', { on = false, timeout = 0 } ) )
114         assert( aClient:setoption( 'tcp-nodelay', true ) )
115         assert( aClient:settimeout( nil ) )
116         setfenv( os.getenv, anEnvironment )
117        
118         aHandler( aTCPServer, aTCPServer )
119
120         aClient:close()
121        
122         return true
123     end
124 end
125
126 function self:__call( aHandler )
127     local aCall = Call( self, aHandler )
128     local aStatus, aResult = xpcall( aCall, debug.traceback )
129    
130     if not aStatus then
131         io.stderr:write( aResult )
132         io.stderr:flush()
133     end
134    
135     return self( aHandler )
136 end
137
138 function self:__concat( aValue )
139     return tostring( self ) .. tostring( aValue )
140 end
141
142 function self:__tostring()
143     local anEnvironment = self.environment
144     local aHost = anEnvironment[ 'TCPLOCALHOST' ] or anEnvironment[ 'TCPLOCALIP' ]
145     local aPort = anEnvironment[ 'TCPLOCALPORT' ]
146    
147     return ( '%s:%s' ):format( tostring( aHost ), tostring( aPort ) )
148 end
149
150 function self:close()
151     -- no op
152 end
153
154 function self:read( aPattern, ... )
155     local aClient = self.client
156     local aBuffer = {}
157    
158     aBuffer[ #aBuffer + 1 ] = assert( aClient:receive( aPattern ) )
159
160     for anIndex = 1, select( '#', ... ) do
161         aBuffer[ #aBuffer + 1 ] = aClient:receive( select( anIndex, ... ) )
162     end
163    
164     return unpack( aBuffer )
165 end
166
167 function self:lines()
168     local aClient = self.client
169    
170     return function()
171         return aClient:receive()
172     end
173 end
174
175 function self:write( ... )
176     local aClient = self.client
177     local aBuffer = {}
178    
179     for anIndex = 1, select( '#', ... ) do
180         aBuffer[ #aBuffer + 1 ] = tostring( select( anIndex, ... ) )
181     end
182
183     return aClient:send( table.concat( aBuffer ) )
184 end
185
186 function self:flush()
187     -- no op
188 end
189
190
Note: See TracBrowser for help on using the browser.