summaryrefslogtreecommitdiff
path: root/src/experimental/fastcgi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/experimental/fastcgi.cpp')
-rw-r--r--src/experimental/fastcgi.cpp372
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"
20using Bu::sio;
21using Bu::Fmt;
22
23Bu::FastCgi::FastCgi() :
24 pSrv( NULL ),
25 bRunning( true )
26{
27 pSrv = new Bu::TcpServerSocket( STDIN_FILENO, false );
28}
29
30Bu::FastCgi::FastCgi( int iPort ) :
31 pSrv( NULL ),
32 bRunning( true )
33{
34 pSrv = new Bu::TcpServerSocket( iPort );
35}
36
37Bu::FastCgi::~FastCgi()
38{
39}
40
41bool 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
67void 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
77void 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
85void 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
91void 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
97uint32_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
110void 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
134bool 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
143Bu::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
155void 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