Skip to content

Commit 87b372a

Browse files
committed
add fastcgi example.
1 parent 9698854 commit 87b372a

7 files changed

Lines changed: 371 additions & 1 deletion

File tree

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
2012-mm-dd Shuo Chen <chenshuo@chenshuo.com>
22
* Add single thread concurrent download example in examples/curl.
33
* Add distributed word counting example.
4+
* Add simple FastCGI example.
45
* Fix HttpRequest for empty header value, contributed by SeasonLee
6+
* Fix Connector destruction
57
* Version 0.9.0
68

79
2012-11-06 Shuo Chen <chenshuo@chenshuo.com>

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_subdirectory(asio/chat)
22
add_subdirectory(asio/tutorial)
3+
add_subdirectory(fastcgi)
34
add_subdirectory(filetransfer)
45
add_subdirectory(hub)
56
add_subdirectory(idleconnection)

examples/curl/download.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class Piece : boost::noncopyable
4646

4747
void onDone(curl::Request* c, int code)
4848
{
49-
LOG_DEBUG << "[" << range_ << "]";
49+
LOG_INFO << "[" << range_ << "] is done";
5050
req_.reset();
5151
out_.reset();
5252
doneCb_();

examples/fastcgi/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
add_executable(fastcgi_test fastcgi.cc fastcgi_test.cc)
2+
target_link_libraries(fastcgi_test muduo_net)
3+

examples/fastcgi/fastcgi.cc

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
#include <examples/fastcgi/fastcgi.h>
2+
#include <muduo/base/Logging.h>
3+
4+
struct FastCgiCodec::RecordHeader
5+
{
6+
uint8_t version;
7+
uint8_t type;
8+
uint16_t id;
9+
uint16_t length;
10+
uint8_t padding;
11+
uint8_t unused;
12+
};
13+
14+
const unsigned FastCgiCodec::kRecordHeader = sizeof(FastCgiCodec::RecordHeader);
15+
16+
enum FcgiType
17+
{
18+
kFcgiInvalid = 0,
19+
kFcgiBeginRequest = 1,
20+
kFcgiAbortRequest = 2,
21+
kFcgiEndRequest = 3,
22+
kFcgiParams = 4,
23+
kFcgiStdin = 5,
24+
kFcgiStdout = 6,
25+
kFcgiStderr = 7,
26+
kFcgiData = 8,
27+
kFcgiGetValues = 9,
28+
kFcgiGetValuesResult = 10,
29+
};
30+
31+
enum FcgiRole
32+
{
33+
// kFcgiInvalid = 0,
34+
kFcgiResponder = 1,
35+
kFcgiAuthorizer = 2,
36+
};
37+
38+
enum FcgiConstant
39+
{
40+
kFcgiKeepConn = 1,
41+
};
42+
43+
using namespace muduo::net;
44+
45+
bool FastCgiCodec::onParams(const char* content, uint16_t length)
46+
{
47+
if (length > 0)
48+
{
49+
paramsStream_.append(content, length);
50+
}
51+
else if (!parseAllParams())
52+
{
53+
LOG_ERROR << "parseAllParams() failed";
54+
return false;
55+
}
56+
return true;
57+
}
58+
59+
void FastCgiCodec::onStdin(const char* content, uint16_t length)
60+
{
61+
if (length > 0)
62+
{
63+
stdin_.append(content, length);
64+
}
65+
else
66+
{
67+
gotRequest_ = true;
68+
}
69+
}
70+
71+
bool FastCgiCodec::parseAllParams()
72+
{
73+
while (paramsStream_.readableBytes() > 0)
74+
{
75+
uint32_t nameLen = readLen();
76+
if (nameLen == static_cast<uint32_t>(-1))
77+
return false;
78+
uint32_t valueLen = readLen();
79+
if (valueLen == static_cast<uint32_t>(-1))
80+
return false;
81+
if (paramsStream_.readableBytes() >= nameLen+valueLen)
82+
{
83+
string name = paramsStream_.retrieveAsString(nameLen);
84+
params_[name] = paramsStream_.retrieveAsString(valueLen);
85+
}
86+
else
87+
{
88+
return false;
89+
}
90+
}
91+
return true;
92+
}
93+
94+
uint32_t FastCgiCodec::readLen()
95+
{
96+
if (paramsStream_.readableBytes() >= 1)
97+
{
98+
uint8_t byte = paramsStream_.peekInt8();
99+
if (byte & 0x80)
100+
{
101+
if (paramsStream_.readableBytes() >= sizeof(uint32_t))
102+
{
103+
return paramsStream_.readInt32() & 0x7fffffff;
104+
}
105+
else
106+
{
107+
return -1;
108+
}
109+
}
110+
else
111+
{
112+
return paramsStream_.readInt8();
113+
}
114+
}
115+
else
116+
{
117+
return -1;
118+
}
119+
}
120+
121+
using muduo::net::Buffer;
122+
123+
void FastCgiCodec::endStdout(Buffer* buf)
124+
{
125+
RecordHeader header =
126+
{
127+
1,
128+
kFcgiStdout,
129+
htons(1),
130+
0,
131+
0,
132+
0,
133+
};
134+
buf->append(&header, kRecordHeader);
135+
}
136+
137+
void FastCgiCodec::endRequest(Buffer* buf)
138+
{
139+
RecordHeader header =
140+
{
141+
1,
142+
kFcgiEndRequest,
143+
htons(1),
144+
htons(kRecordHeader),
145+
0,
146+
0,
147+
};
148+
buf->append(&header, kRecordHeader);
149+
buf->appendInt32(0);
150+
buf->appendInt32(0);
151+
}
152+
153+
void FastCgiCodec::respond(Buffer* response)
154+
{
155+
if (response->readableBytes() < 65536
156+
&& response->prependableBytes() >= kRecordHeader)
157+
{
158+
RecordHeader header =
159+
{
160+
1,
161+
kFcgiStdout,
162+
htons(1),
163+
htons(static_cast<uint16_t>(response->readableBytes())),
164+
static_cast<uint8_t>(-response->readableBytes() & 7),
165+
0,
166+
};
167+
response->prepend(&header, kRecordHeader);
168+
response->append("\0\0\0\0\0\0\0\0", header.padding);
169+
}
170+
else
171+
{
172+
// FIXME:
173+
}
174+
175+
endStdout(response);
176+
endRequest(response);
177+
}
178+
179+
bool FastCgiCodec::parseRequest(Buffer* buf)
180+
{
181+
while (buf->readableBytes() >= kRecordHeader)
182+
{
183+
RecordHeader header;
184+
memcpy(&header, buf->peek(), kRecordHeader);
185+
header.id = ntohs(header.id);
186+
header.length = ntohs(header.length);
187+
size_t total = kRecordHeader + header.length + header.padding;
188+
if (buf->readableBytes() >= total)
189+
{
190+
switch (header.type)
191+
{
192+
case kFcgiBeginRequest:
193+
onBeginRequest(header, buf);
194+
// FIXME: check
195+
break;
196+
case kFcgiParams:
197+
onParams(buf->peek() + kRecordHeader, header.length);
198+
// FIXME: check
199+
break;
200+
case kFcgiStdin:
201+
onStdin(buf->peek() + kRecordHeader, header.length);
202+
break;
203+
case kFcgiData:
204+
// FIXME:
205+
break;
206+
case kFcgiGetValues:
207+
// FIXME:
208+
break;
209+
default:
210+
// FIXME:
211+
break;
212+
}
213+
buf->retrieve(total);
214+
}
215+
else
216+
{
217+
break;
218+
}
219+
}
220+
return true;
221+
}
222+
223+
uint16_t readInt16(const void* p)
224+
{
225+
uint16_t be16 = 0;
226+
::memcpy(&be16, p, sizeof be16);
227+
return ntohs(be16);
228+
}
229+
230+
bool FastCgiCodec::onBeginRequest(const RecordHeader& header, const Buffer* buf)
231+
{
232+
assert(buf->readableBytes() >= header.length);
233+
assert(header.type == kFcgiBeginRequest);
234+
235+
if (header.length >= kRecordHeader)
236+
{
237+
uint16_t role = readInt16(buf->peek()+kRecordHeader);
238+
uint8_t flags = buf->peek()[kRecordHeader + sizeof(int16_t)];
239+
if (role == kFcgiResponder)
240+
{
241+
keepConn_ = flags == kFcgiKeepConn;
242+
return true;
243+
}
244+
}
245+
return false;
246+
}

examples/fastcgi/fastcgi.h

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#ifndef MUDUO_EXAMPLES_FASTCGI_FASTCGI_H
2+
#define MUDUO_EXAMPLES_FASTCGI_FASTCGI_H
3+
4+
#include <muduo/net/TcpConnection.h>
5+
#include <map>
6+
7+
using muduo::string;
8+
9+
// one FastCgiCodec per TcpConnection
10+
// both lighttpd and nginx do not implement multiplexing,
11+
// so there is no concurrent requests of one connection.
12+
class FastCgiCodec : boost::noncopyable
13+
{
14+
public:
15+
typedef std::map<string, string> ParamMap;
16+
typedef boost::function<void (const muduo::net::TcpConnectionPtr& conn,
17+
ParamMap&,
18+
muduo::net::Buffer*)> Callback;
19+
20+
explicit FastCgiCodec(const Callback& cb)
21+
: cb_(cb),
22+
gotRequest_(false),
23+
keepConn_(false)
24+
{
25+
}
26+
27+
void onMessage(const muduo::net::TcpConnectionPtr& conn,
28+
muduo::net::Buffer* buf,
29+
muduo::Timestamp receiveTime)
30+
{
31+
parseRequest(buf);
32+
if (gotRequest_)
33+
{
34+
cb_(conn, params_, &stdin_);
35+
stdin_.retrieveAll();
36+
paramsStream_.retrieveAll();
37+
params_.clear();
38+
gotRequest_ = false;
39+
if (!keepConn_)
40+
{
41+
conn->shutdown();
42+
}
43+
}
44+
}
45+
46+
static void respond(muduo::net::Buffer* response);
47+
48+
private:
49+
struct RecordHeader;
50+
bool parseRequest(muduo::net::Buffer* buf);
51+
bool onBeginRequest(const RecordHeader& header, const muduo::net::Buffer* buf);
52+
void onStdin(const char* content, uint16_t length);
53+
bool onParams(const char* content, uint16_t length);
54+
bool parseAllParams();
55+
uint32_t readLen();
56+
57+
static void endStdout(muduo::net::Buffer* buf);
58+
static void endRequest(muduo::net::Buffer* buf);
59+
60+
Callback cb_;
61+
bool gotRequest_;
62+
bool keepConn_;
63+
muduo::net::Buffer stdin_;
64+
muduo::net::Buffer paramsStream_;
65+
ParamMap params_;
66+
67+
const static unsigned kRecordHeader;
68+
};
69+
70+
#endif // MUDUO_EXAMPLES_FASTCGI_FASTCGI_H

examples/fastcgi/fastcgi_test.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <examples/fastcgi/fastcgi.h>
2+
3+
#include <muduo/base/Logging.h>
4+
#include <muduo/net/EventLoop.h>
5+
#include <muduo/net/TcpServer.h>
6+
7+
#include <boost/bind.hpp>
8+
9+
using namespace muduo::net;
10+
11+
void onRequest(const TcpConnectionPtr& conn,
12+
FastCgiCodec::ParamMap& params,
13+
Buffer* in)
14+
{
15+
LOG_INFO << params["REQUEST_URI"];
16+
17+
for (FastCgiCodec::ParamMap::const_iterator it = params.begin();
18+
it != params.end(); ++it)
19+
{
20+
LOG_DEBUG << it->first << " = " << it->second;
21+
}
22+
Buffer response;
23+
response.append("Context-Type: text/plain\r\n\r\n");
24+
response.append("Hello FastCGI.");
25+
FastCgiCodec::respond(&response);
26+
conn->send(&response);
27+
}
28+
29+
typedef boost::shared_ptr<FastCgiCodec> CodecPtr;
30+
void onConnection(const TcpConnectionPtr& conn)
31+
{
32+
if (conn->connected())
33+
{
34+
CodecPtr codec(new FastCgiCodec(onRequest));
35+
conn->setContext(codec);
36+
conn->setMessageCallback(
37+
boost::bind(&FastCgiCodec::onMessage, codec, _1, _2, _3));
38+
}
39+
}
40+
41+
int main()
42+
{
43+
muduo::net::EventLoop loop;
44+
TcpServer server(&loop, InetAddress(9000), "FastCGI");
45+
server.setConnectionCallback(onConnection);
46+
server.start();
47+
loop.loop();
48+
}

0 commit comments

Comments
 (0)