diff options
Diffstat (limited to 'src/experimental/fastcgi.cpp')
-rw-r--r-- | src/experimental/fastcgi.cpp | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/src/experimental/fastcgi.cpp b/src/experimental/fastcgi.cpp new file mode 100644 index 0000000..2f9161e --- /dev/null +++ b/src/experimental/fastcgi.cpp | |||
@@ -0,0 +1,372 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007-2011 Xagasoft, All rights reserved. | ||
3 | * | ||
4 | * This file is part of the libbu++ library and is released under the | ||
5 | * terms of the license contained in the file LICENSE. | ||
6 | */ | ||
7 | |||
8 | #include "bu/fastcgi.h" | ||
9 | |||
10 | #ifndef WIN32 | ||
11 | #include <arpa/inet.h> | ||
12 | #endif | ||
13 | |||
14 | #include <errno.h> | ||
15 | #include <unistd.h> | ||
16 | |||
17 | #include "bu/membuf.h" | ||
18 | |||
19 | #include "bu/sio.h" | ||
20 | using Bu::sio; | ||
21 | using Bu::Fmt; | ||
22 | |||
23 | Bu::FastCgi::FastCgi() : | ||
24 | pSrv( NULL ), | ||
25 | bRunning( true ) | ||
26 | { | ||
27 | pSrv = new Bu::TcpServerSocket( STDIN_FILENO, false ); | ||
28 | } | ||
29 | |||
30 | Bu::FastCgi::FastCgi( int iPort ) : | ||
31 | pSrv( NULL ), | ||
32 | bRunning( true ) | ||
33 | { | ||
34 | pSrv = new Bu::TcpServerSocket( iPort ); | ||
35 | } | ||
36 | |||
37 | Bu::FastCgi::~FastCgi() | ||
38 | { | ||
39 | } | ||
40 | |||
41 | bool Bu::FastCgi::isEmbedded() | ||
42 | { | ||
43 | #ifndef WIN32 | ||
44 | struct sockaddr name; | ||
45 | socklen_t namelen = sizeof(name); | ||
46 | if( getpeername( STDIN_FILENO, &name, &namelen ) != 0 && | ||
47 | errno == ENOTCONN ) | ||
48 | { | ||
49 | sio << "errno = " << errno << " (" << strerror( errno ) << ")" << | ||
50 | sio.nl; | ||
51 | sio << "Socket found" << sio.nl; | ||
52 | return true; | ||
53 | } | ||
54 | else | ||
55 | { | ||
56 | sio << "errno = " << errno << " (" << strerror( errno ) << ")" << | ||
57 | sio.nl; | ||
58 | sio << "No socket detected, running in standalone mode" << sio.nl; | ||
59 | return false; | ||
60 | } | ||
61 | #else | ||
62 | #warning Bu::FastCgi::isEmbedded IS A STUB for WIN32!!!! | ||
63 | return false; | ||
64 | #endif | ||
65 | } | ||
66 | |||
67 | void Bu::FastCgi::read( Bu::TcpSocket &s, Bu::FastCgi::Record &r ) | ||
68 | { | ||
69 | int iRead = s.read( &r, sizeof(Record) ); | ||
70 | if( iRead != sizeof(Record) ) | ||
71 | throw Bu::TcpSocketException("Hey, the size %d is wrong for Record. (%s)", | ||
72 | iRead, strerror( errno ) ); | ||
73 | r.uRequestId = ntohs( r.uRequestId ); | ||
74 | r.uContentLength = ntohs( r.uContentLength ); | ||
75 | } | ||
76 | |||
77 | void Bu::FastCgi::write( Bu::TcpSocket &s, Bu::FastCgi::Record r ) | ||
78 | { | ||
79 | // sio << "Out -> " << r << sio.nl; | ||
80 | r.uRequestId = htons( r.uRequestId ); | ||
81 | r.uContentLength = htons( r.uContentLength ); | ||
82 | s.write( &r, sizeof(Record) ); | ||
83 | } | ||
84 | |||
85 | void Bu::FastCgi::read( Bu::TcpSocket &s, Bu::FastCgi::BeginRequestBody &b ) | ||
86 | { | ||
87 | s.read( &b, sizeof(BeginRequestBody) ); | ||
88 | b.uRole = ntohs( b.uRole ); | ||
89 | } | ||
90 | |||
91 | void Bu::FastCgi::write( Bu::TcpSocket &s, Bu::FastCgi::EndRequestBody b ) | ||
92 | { | ||
93 | b.uStatus = htonl( b.uStatus ); | ||
94 | s.write( &b, sizeof(b) ); | ||
95 | } | ||
96 | |||
97 | uint32_t Bu::FastCgi::readLen( Bu::TcpSocket &s, uint16_t &uRead ) | ||
98 | { | ||
99 | uint8_t uByte[4]; | ||
100 | s.read( uByte, 1 ); | ||
101 | uRead++; | ||
102 | if( uByte[0] >> 7 == 0 ) | ||
103 | return uByte[0]; | ||
104 | |||
105 | s.read( uByte+1, 3 ); | ||
106 | uRead += 3; | ||
107 | return ((uByte[0]&0x7f)<<24)|(uByte[1]<<16)|(uByte[2]<<8)|(uByte[3]); | ||
108 | } | ||
109 | |||
110 | void Bu::FastCgi::readPair( Bu::TcpSocket &s, StrHash &hParams, uint16_t &uRead ) | ||
111 | { | ||
112 | uint32_t uName = readLen( s, uRead ); | ||
113 | uint32_t uValue = readLen( s, uRead ); | ||
114 | uRead += uName + uValue; | ||
115 | unsigned char *sName = new unsigned char[uName]; | ||
116 | s.read( sName, uName ); | ||
117 | Bu::String fsName( (char *)sName, uName ); | ||
118 | delete[] sName; | ||
119 | |||
120 | if( uValue > 0 ) | ||
121 | { | ||
122 | unsigned char *sValue = new unsigned char[uValue]; | ||
123 | s.read( sValue, uValue ); | ||
124 | Bu::String fsValue( (char *)sValue, uValue ); | ||
125 | hParams.insert( fsName, fsValue ); | ||
126 | delete[] sValue; | ||
127 | } | ||
128 | else | ||
129 | { | ||
130 | hParams.insert( fsName, "" ); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | bool Bu::FastCgi::hasChannel( int iChan ) | ||
135 | { | ||
136 | if( aChannel.getSize() < iChan ) | ||
137 | return false; | ||
138 | if( aChannel[iChan-1] == NULL ) | ||
139 | return false; | ||
140 | return true; | ||
141 | } | ||
142 | |||
143 | Bu::Formatter &Bu::operator<<( Bu::Formatter &f, const Bu::FastCgi::Record &r ) | ||
144 | { | ||
145 | f << "[Ver=" << (uint32_t)r.uVersion << | ||
146 | ", Type=" << (uint32_t)r.uType << | ||
147 | ", Req=" << (uint32_t)r.uRequestId << | ||
148 | ", clen=" << (uint32_t)r.uContentLength << | ||
149 | ", plen=" << (uint32_t)r.uPaddingLength << | ||
150 | ", resv=" << (uint32_t)r.uReserved << | ||
151 | "]"; | ||
152 | return f; | ||
153 | } | ||
154 | |||
155 | void Bu::FastCgi::run() | ||
156 | { | ||
157 | // sio << "sizeof(Bu::FastCgi::Record) = " << sizeof(Record) << sio.nl; | ||
158 | bRunning = true; | ||
159 | while( bRunning ) | ||
160 | { | ||
161 | int iSock = pSrv->accept( 5 ); | ||
162 | if( iSock < 0 ) | ||
163 | continue; | ||
164 | |||
165 | Bu::TcpSocket s( iSock ); | ||
166 | s.setBlocking( true ); | ||
167 | // sio << "Got connection, blocking? " << s.isBlocking() << sio.nl; | ||
168 | try | ||
169 | { | ||
170 | for(;;) | ||
171 | { | ||
172 | Record r; | ||
173 | memset( &r, 0, sizeof(r) ); | ||
174 | // try | ||
175 | // { | ||
176 | read( s, r ); | ||
177 | // } | ||
178 | // catch( Bu::ExceptionBase &e ) | ||
179 | // { | ||
180 | // sio << "Error: " << e.what() << ", " << s.isOpen() << | ||
181 | // sio.nl; | ||
182 | // continue; | ||
183 | // } | ||
184 | Channel *pChan = NULL; | ||
185 | if( r.uRequestId > 0 ) | ||
186 | { | ||
187 | if( !hasChannel( r.uRequestId ) && | ||
188 | r.uType != typeBeginRequest ) | ||
189 | { | ||
190 | sio << "Error, stream data without stream." << sio.nl; | ||
191 | sio << r << sio.nl; | ||
192 | if( r.uContentLength > 0 ) | ||
193 | { | ||
194 | char *buf = new char[r.uContentLength]; | ||
195 | sio << " (read " << s.read( buf, r.uContentLength ) | ||
196 | << "/" << r.uContentLength << "):" << sio.nl; | ||
197 | sio.write( buf, r.uContentLength ); | ||
198 | sio << sio.nl << sio.nl; | ||
199 | } | ||
200 | } | ||
201 | while( aChannel.getSize() < r.uRequestId ) | ||
202 | aChannel.append( NULL ); | ||
203 | if( r.uRequestId > 0 ) | ||
204 | pChan = aChannel[r.uRequestId-1]; | ||
205 | } | ||
206 | |||
207 | // sio << "Record (id=" << r.uRequestId << ", len=" << | ||
208 | // r.uContentLength << ", pad=" << | ||
209 | // (unsigned int)r.uPaddingLength << "): "; | ||
210 | // fflush( stdout ); | ||
211 | |||
212 | switch( (RequestType)r.uType ) | ||
213 | { | ||
214 | case typeBeginRequest: | ||
215 | // sio << "Begin Request."; | ||
216 | { | ||
217 | BeginRequestBody b; | ||
218 | read( s, b ); | ||
219 | if( pChan != NULL ) | ||
220 | { | ||
221 | sio << "Error!!!" << sio.nl; | ||
222 | return; | ||
223 | } | ||
224 | pChan = aChannel[r.uRequestId-1] = new Channel(); | ||
225 | } | ||
226 | break; | ||
227 | |||
228 | case typeParams: | ||
229 | // sio << "Params."; | ||
230 | if( r.uContentLength == 0 ) | ||
231 | { | ||
232 | pChan->uFlags |= chflgParamsDone; | ||
233 | } | ||
234 | else | ||
235 | { | ||
236 | uint16_t uUsed = 0; | ||
237 | while( uUsed < r.uContentLength ) | ||
238 | { | ||
239 | readPair( s, pChan->hParams, uUsed ); | ||
240 | } | ||
241 | } | ||
242 | break; | ||
243 | |||
244 | case typeStdIn: | ||
245 | // sio << "StdIn."; | ||
246 | if( r.uContentLength == 0 ) | ||
247 | { | ||
248 | pChan->uFlags |= chflgStdInDone; | ||
249 | } | ||
250 | else | ||
251 | { | ||
252 | char *buf = new char[r.uContentLength]; | ||
253 | int iTotal = 0; | ||
254 | do | ||
255 | { | ||
256 | size_t iRead = s.read( | ||
257 | buf, r.uContentLength-iTotal ); | ||
258 | iTotal += iRead; | ||
259 | // sio << " (read " << iRead << " " << iTotal | ||
260 | // << "/" << r.uContentLength << ")"; | ||
261 | pChan->sStdIn.append( buf, iRead ); | ||
262 | } while( iTotal < r.uContentLength ); | ||
263 | delete[] buf; | ||
264 | } | ||
265 | break; | ||
266 | |||
267 | case typeData: | ||
268 | // sio << "Data."; | ||
269 | if( r.uContentLength == 0 ) | ||
270 | { | ||
271 | pChan->uFlags |= chflgDataDone; | ||
272 | } | ||
273 | else | ||
274 | { | ||
275 | char *buf = new char[r.uContentLength]; | ||
276 | s.read( buf, r.uContentLength ); | ||
277 | pChan->sData.append( buf, r.uContentLength ); | ||
278 | delete[] buf; | ||
279 | } | ||
280 | break; | ||
281 | |||
282 | case typeStdOut: | ||
283 | case typeStdErr: | ||
284 | case typeEndRequest: | ||
285 | case typeAbortRequest: | ||
286 | case typeGetValuesResult: | ||
287 | // sio << "Scary."; | ||
288 | // ??? we shouldn't get these. | ||
289 | break; | ||
290 | |||
291 | case typeGetValues: | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | // sio << sio.nl; | ||
296 | |||
297 | if( pChan ) | ||
298 | { | ||
299 | if( pChan->uFlags == chflgAllDone ) | ||
300 | { | ||
301 | // sio << "All done, generating output." << sio.nl; | ||
302 | Bu::MemBuf mStdOut, mStdErr; | ||
303 | int iRet = onRequest( | ||
304 | pChan->hParams, pChan->sStdIn, | ||
305 | mStdOut, mStdErr | ||
306 | ); | ||
307 | |||
308 | Bu::String &sStdOut = mStdOut.getString(); | ||
309 | Bu::String &sStdErr = mStdErr.getString(); | ||
310 | |||
311 | Record rOut; | ||
312 | memset( &rOut, 0, sizeof(rOut) ); | ||
313 | rOut.uVersion = 1; | ||
314 | rOut.uRequestId = r.uRequestId; | ||
315 | rOut.uPaddingLength = 0; | ||
316 | rOut.uType = typeStdOut; | ||
317 | if( sStdOut.getSize() > 0 ) | ||
318 | { | ||
319 | for( int iPos = 0; iPos < sStdOut.getSize(); | ||
320 | iPos += 65528 ) | ||
321 | { | ||
322 | int iSize = sStdOut.getSize()-iPos; | ||
323 | if( iSize > 65528 ) | ||
324 | iSize = 65528; | ||
325 | rOut.uContentLength = iSize; | ||
326 | write( s, rOut ); | ||
327 | s.write( sStdOut.getStr()+iPos, iSize ); | ||
328 | } | ||
329 | } | ||
330 | rOut.uContentLength = 0; | ||
331 | write( s, rOut ); | ||
332 | |||
333 | rOut.uType = typeStdErr; | ||
334 | if( sStdErr.getSize() > 0 ) | ||
335 | { | ||
336 | for( int iPos = 0; iPos < sStdErr.getSize(); | ||
337 | iPos += 65528 ) | ||
338 | { | ||
339 | int iSize = sStdErr.getSize()-iPos; | ||
340 | if( iSize > 65528 ) | ||
341 | iSize = 65528; | ||
342 | rOut.uContentLength = iSize; | ||
343 | write( s, rOut ); | ||
344 | s.write( sStdErr.getStr()+iPos, iSize ); | ||
345 | } | ||
346 | } | ||
347 | rOut.uContentLength = 0; | ||
348 | write( s, rOut ); | ||
349 | |||
350 | rOut.uType = typeEndRequest; | ||
351 | rOut.uContentLength = 8; | ||
352 | write( s, rOut ); | ||
353 | |||
354 | EndRequestBody b; | ||
355 | memset( &b, 0, sizeof(b) ); | ||
356 | b.uStatus = iRet; | ||
357 | write( s, b ); | ||
358 | |||
359 | delete pChan; | ||
360 | aChannel[r.uRequestId-1] = NULL; | ||
361 | } | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | catch( Bu::TcpSocketException &e ) | ||
366 | { | ||
367 | // sio << "Bu::SocketException: " << e.what() << sio.nl << | ||
368 | // "\tSocket open: " << s.isOpen() << sio.nl; | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||