diff options
author | Mike Buland <eichlan@xagasoft.com> | 2007-10-08 04:11:56 +0000 |
---|---|---|
committer | Mike Buland <eichlan@xagasoft.com> | 2007-10-08 04:11:56 +0000 |
commit | fe1862511da4a6d66e2f11ba633c035c3cf7b14f (patch) | |
tree | 5b7b4d863c0b26390ae7f786b23485341c0ebd50 /src | |
parent | d8ba63fd2b512297b42b8b7db877205ab287e067 (diff) | |
download | libbu++-fe1862511da4a6d66e2f11ba633c035c3cf7b14f.tar.gz libbu++-fe1862511da4a6d66e2f11ba633c035c3cf7b14f.tar.bz2 libbu++-fe1862511da4a6d66e2f11ba633c035c3cf7b14f.tar.xz libbu++-fe1862511da4a6d66e2f11ba633c035c3cf7b14f.zip |
Added some helpers and fixes to Bu::Client, also got all the basics of a
general Http handler working, the test for the moment, is Doxysrv, I'll probably
write a cute little stand-alone one in libbu++ later as a demo/test.
Diffstat (limited to '')
-rw-r--r-- | src/client.cpp | 5 | ||||
-rw-r--r-- | src/client.h | 1 | ||||
-rw-r--r-- | src/protocolhttp.cpp | 194 | ||||
-rw-r--r-- | src/protocolhttp.h | 27 |
4 files changed, 216 insertions, 11 deletions
diff --git a/src/client.cpp b/src/client.cpp index 19d2778..ee301e9 100644 --- a/src/client.cpp +++ b/src/client.cpp | |||
@@ -103,6 +103,11 @@ bool Bu::Client::isOpen() | |||
103 | return pSocket->isOpen(); | 103 | return pSocket->isOpen(); |
104 | } | 104 | } |
105 | 105 | ||
106 | void Bu::Client::write( const Bu::FString &sData ) | ||
107 | { | ||
108 | sWriteBuf += sData; | ||
109 | } | ||
110 | |||
106 | void Bu::Client::write( const void *pData, int nBytes ) | 111 | void Bu::Client::write( const void *pData, int nBytes ) |
107 | { | 112 | { |
108 | sWriteBuf.append( (const char *)pData, nBytes ); | 113 | sWriteBuf.append( (const char *)pData, nBytes ); |
diff --git a/src/client.h b/src/client.h index 0b670e2..517131a 100644 --- a/src/client.h +++ b/src/client.h | |||
@@ -25,6 +25,7 @@ namespace Bu | |||
25 | 25 | ||
26 | Bu::FString &getInput(); | 26 | Bu::FString &getInput(); |
27 | Bu::FString &getOutput(); | 27 | Bu::FString &getOutput(); |
28 | void write( const Bu::FString &sData ); | ||
28 | void write( const void *pData, int nBytes ); | 29 | void write( const void *pData, int nBytes ); |
29 | void write( int8_t nData ); | 30 | void write( int8_t nData ); |
30 | void write( int16_t nData ); | 31 | void write( int16_t nData ); |
diff --git a/src/protocolhttp.cpp b/src/protocolhttp.cpp index d18d73d..2f57bdb 100644 --- a/src/protocolhttp.cpp +++ b/src/protocolhttp.cpp | |||
@@ -1,7 +1,8 @@ | |||
1 | #include <dirent.h> | 1 | #include <dirent.h> |
2 | #include <sys/wait.h> | 2 | #include <sys/wait.h> |
3 | #include <errno.h> | 3 | #include <errno.h> |
4 | #include "protocolhttp.h" | 4 | #include "bu/protocolhttp.h" |
5 | #include "bu/logger.h" | ||
5 | 6 | ||
6 | #define CRLF "\x0D\x0A" | 7 | #define CRLF "\x0D\x0A" |
7 | #define CR '\x0D' | 8 | #define CR '\x0D' |
@@ -28,6 +29,13 @@ void Bu::ProtocolHttp::onNewConnection( Bu::Client *pClient ) | |||
28 | 29 | ||
29 | void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) | 30 | void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) |
30 | { | 31 | { |
32 | logHexDump( | ||
33 | 1, | ||
34 | pClient->getInput().getStr(), | ||
35 | pClient->getInput().getSize(), | ||
36 | "input" | ||
37 | ); | ||
38 | |||
31 | for(;;) | 39 | for(;;) |
32 | { | 40 | { |
33 | Bu::FString sToken; | 41 | Bu::FString sToken; |
@@ -38,19 +46,19 @@ void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) | |||
38 | 46 | ||
39 | switch( iState ) | 47 | switch( iState ) |
40 | { | 48 | { |
41 | case 0: // Initial header | 49 | case 0: // Start token, should be "method" (get, put, etc) |
42 | SDB( 0 ); | 50 | SDB( 0 ); |
43 | sMethod = sToken; | 51 | sMethod = sToken; |
44 | iState = 1; | 52 | iState = 1; |
45 | break; | 53 | break; |
46 | 54 | ||
47 | case 1: // Misc headers | 55 | case 1: // The path requested |
48 | SDB( 1 ); | 56 | SDB( 1 ); |
49 | sPath = sToken; | 57 | sPath = sToken; |
50 | iState = 2; | 58 | iState = 2; |
51 | break; | 59 | break; |
52 | 60 | ||
53 | case 2: // Content | 61 | case 2: // The protocol name and version |
54 | SDB( 2 ); | 62 | SDB( 2 ); |
55 | if( strncmp( sToken.getStr(), "HTTP/", 5 ) ) | 63 | if( strncmp( sToken.getStr(), "HTTP/", 5 ) ) |
56 | { | 64 | { |
@@ -69,17 +77,62 @@ void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) | |||
69 | } | 77 | } |
70 | break; | 78 | break; |
71 | 79 | ||
72 | case 3: | 80 | case 3: // End of initial header, now comes mime-style blocks. |
73 | SDB( 3 ); | 81 | SDB( 3 ); |
74 | if( iState != ttNewline ) | 82 | if( tt == ttNewline ) |
83 | { | ||
84 | iState = 10; | ||
85 | } | ||
86 | else if( tt == ttDoubleNewline ) | ||
87 | { | ||
88 | earlyResponse(); | ||
89 | } | ||
90 | else | ||
75 | { | 91 | { |
76 | pClient->disconnect(); | 92 | pClient->disconnect(); |
77 | return; | 93 | return; |
78 | } | 94 | } |
79 | iState = 4; | ||
80 | break; | 95 | break; |
81 | 96 | ||
82 | case 4: | 97 | case 10: // HTTP-Message (skipped for now...) |
98 | SDB( 10 ); | ||
99 | if( tt == ttString ) | ||
100 | { | ||
101 | iState = 11; | ||
102 | } | ||
103 | else | ||
104 | { | ||
105 | pClient->disconnect(); | ||
106 | } | ||
107 | break; | ||
108 | |||
109 | case 11: // Should be a colon... | ||
110 | SDB( 11 ); | ||
111 | if( tt == ttSeperator && sToken == ":" ) | ||
112 | { | ||
113 | iState = 12; | ||
114 | } | ||
115 | else | ||
116 | { | ||
117 | pClient->disconnect(); | ||
118 | } | ||
119 | break; | ||
120 | |||
121 | case 12: | ||
122 | SDB( 12 ); | ||
123 | if( tt == ttNewline ) | ||
124 | { | ||
125 | iState = 10; | ||
126 | } | ||
127 | if( tt == ttDoubleNewline ) | ||
128 | { | ||
129 | earlyResponse(); | ||
130 | } | ||
131 | break; | ||
132 | |||
133 | case 20: | ||
134 | SDB( 20 ); | ||
135 | printf("Content!"); | ||
83 | break; | 136 | break; |
84 | } | 137 | } |
85 | } | 138 | } |
@@ -118,11 +171,11 @@ Bu::ProtocolHttp::TokenType Bu::ProtocolHttp::getToken( Bu::FString &line ) | |||
118 | } | 171 | } |
119 | else if( s == CR ) | 172 | else if( s == CR ) |
120 | { | 173 | { |
121 | if( pClient->getInputSize() < 3 ) | 174 | if( pClient->getInputSize() < 4 ) |
122 | return ttOutOfData; | 175 | return ttOutOfData; |
123 | 176 | ||
124 | char ss[2]; | 177 | char ss[3]; |
125 | pClient->peek( ss, 2, j+1 ); | 178 | pClient->peek( ss, 3, j+1 ); |
126 | if( ss[0] == LF && ss[1] != ' ' && ss[1] != '\t' ) | 179 | if( ss[0] == LF && ss[1] != ' ' && ss[1] != '\t' ) |
127 | { | 180 | { |
128 | if( bNonWS ) | 181 | if( bNonWS ) |
@@ -130,6 +183,11 @@ Bu::ProtocolHttp::TokenType Bu::ProtocolHttp::getToken( Bu::FString &line ) | |||
130 | pClient->seek( j ); | 183 | pClient->seek( j ); |
131 | return ttString; | 184 | return ttString; |
132 | } | 185 | } |
186 | else if( ss[1] == CR && ss[2] == LF ) | ||
187 | { | ||
188 | pClient->seek( 4 ); | ||
189 | return ttDoubleNewline; | ||
190 | } | ||
133 | else | 191 | else |
134 | { | 192 | { |
135 | pClient->seek( 2 ); | 193 | pClient->seek( 2 ); |
@@ -168,3 +226,117 @@ bool Bu::ProtocolHttp::isSeperator( char buf ) | |||
168 | buf == '}' ); | 226 | buf == '}' ); |
169 | } | 227 | } |
170 | 228 | ||
229 | void Bu::ProtocolHttp::earlyResponse() | ||
230 | { | ||
231 | if( sMethod == "GET" ) | ||
232 | { | ||
233 | onRequest( sMethod, sPath ); | ||
234 | } | ||
235 | else | ||
236 | { | ||
237 | iState = 20; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | void Bu::ProtocolHttp::lateResponse() | ||
242 | { | ||
243 | onRequest( sMethod, sPath ); | ||
244 | } | ||
245 | |||
246 | void Bu::ProtocolHttp::sendResponse( const Response &rRes ) | ||
247 | { | ||
248 | char buf[1024]; | ||
249 | int iSize = sprintf( buf, "HTTP/1.1 %d ", rRes.iCode ); | ||
250 | |||
251 | pClient->write( buf, iSize ); | ||
252 | pClient->write( rRes.sReason ); | ||
253 | pClient->write( CRLF, 2 ); | ||
254 | |||
255 | for( Response::StringHash::const_iterator i = rRes.hHeaders.begin(); | ||
256 | i != rRes.hHeaders.end(); i++ ) | ||
257 | { | ||
258 | pClient->write( i.getKey() ); | ||
259 | pClient->write(": ", 2 ); | ||
260 | pClient->write( i.getValue() ); | ||
261 | pClient->write( CRLF, 2 ); | ||
262 | } | ||
263 | |||
264 | iSize = sprintf( buf, "Content-Length: %ld" CRLF, rRes.sContent.getSize() ); | ||
265 | pClient->write( buf, iSize ); | ||
266 | |||
267 | pClient->write( CRLF, 2 ); | ||
268 | pClient->write( rRes.sContent ); | ||
269 | } | ||
270 | |||
271 | // | ||
272 | // Bu::ProtocolHttp::Response | ||
273 | // | ||
274 | Bu::ProtocolHttp::Response::Response( int iCode ) : | ||
275 | iCode( iCode ) | ||
276 | { | ||
277 | switch( iCode ) | ||
278 | { | ||
279 | case 100: sReason = "Continue"; break; | ||
280 | case 101: sReason = "Switching Protocols"; break; | ||
281 | case 200: sReason = "OK"; break; | ||
282 | case 201: sReason = "Created"; break; | ||
283 | case 202: sReason = "Accepted"; break; | ||
284 | case 203: sReason = "Non-Authoritative Information"; break; | ||
285 | case 204: sReason = "No Content"; break; | ||
286 | case 205: sReason = "Reset Content"; break; | ||
287 | case 206: sReason = "Partial Content"; break; | ||
288 | case 300: sReason = "Multiple Choices"; break; | ||
289 | case 301: sReason = "Moved Permanently"; break; | ||
290 | case 302: sReason = "Found"; break; | ||
291 | case 303: sReason = "See Other"; break; | ||
292 | case 304: sReason = "Not Modified"; break; | ||
293 | case 305: sReason = "Use Proxy"; break; | ||
294 | case 307: sReason = "Temporary Redirect"; break; | ||
295 | case 400: sReason = "Bad Request"; break; | ||
296 | case 401: sReason = "Unauthorized"; break; | ||
297 | case 402: sReason = "Payment Required"; break; | ||
298 | case 403: sReason = "Forbidden"; break; | ||
299 | case 404: sReason = "Not Found"; break; | ||
300 | case 405: sReason = "Method Not Allowed"; break; | ||
301 | case 406: sReason = "Not Acceptable"; break; | ||
302 | case 407: sReason = "Proxy Authentication Required"; break; | ||
303 | case 408: sReason = "Request Time-out"; break; | ||
304 | case 409: sReason = "Conflict"; break; | ||
305 | case 410: sReason = "Gone"; break; | ||
306 | case 411: sReason = "Length Required"; break; | ||
307 | case 412: sReason = "Precondition Failed"; break; | ||
308 | case 413: sReason = "Request Entity Too Large"; break; | ||
309 | case 414: sReason = "Request-URI Too Large"; break; | ||
310 | case 415: sReason = "Unsupported Media Type"; break; | ||
311 | case 416: sReason = "Requested range not satisfiable"; break; | ||
312 | case 417: sReason = "Expectation Failed"; break; | ||
313 | case 500: sReason = "Internal Server Error"; break; | ||
314 | case 501: sReason = "Not Implemented"; break; | ||
315 | case 502: sReason = "Bad Gateway"; break; | ||
316 | case 503: sReason = "Service Unavailable"; break; | ||
317 | case 504: sReason = "Gateway Time-out"; break; | ||
318 | case 505: sReason = "HTTP Version not supported"; break; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | Bu::ProtocolHttp::Response::Response( int iCode, const Bu::FString &sReason ) : | ||
323 | iCode( iCode ), | ||
324 | sReason( sReason ) | ||
325 | { | ||
326 | } | ||
327 | |||
328 | Bu::ProtocolHttp::Response::~Response() | ||
329 | { | ||
330 | } | ||
331 | |||
332 | void Bu::ProtocolHttp::Response::setHeader( | ||
333 | const Bu::FString &sKey, const Bu::FString &sVal ) | ||
334 | { | ||
335 | hHeaders.insert( sKey, sVal ); | ||
336 | } | ||
337 | |||
338 | void Bu::ProtocolHttp::Response::setContent( const Bu::FString &sCont ) | ||
339 | { | ||
340 | sContent = sCont; | ||
341 | } | ||
342 | |||
diff --git a/src/protocolhttp.h b/src/protocolhttp.h index e10cb23..e2612f5 100644 --- a/src/protocolhttp.h +++ b/src/protocolhttp.h | |||
@@ -24,6 +24,30 @@ namespace Bu | |||
24 | 24 | ||
25 | virtual void onNewConnection( Bu::Client *pClient ); | 25 | virtual void onNewConnection( Bu::Client *pClient ); |
26 | virtual void onNewData( Bu::Client *pClient ); | 26 | virtual void onNewData( Bu::Client *pClient ); |
27 | |||
28 | virtual void onRequest( | ||
29 | const Bu::FString &sMethod, const Bu::FString &sPath )=0; | ||
30 | |||
31 | class Response | ||
32 | { | ||
33 | friend class Bu::ProtocolHttp; | ||
34 | public: | ||
35 | Response( int iCode ); | ||
36 | Response( int iCode, const Bu::FString &sReason ); | ||
37 | virtual ~Response(); | ||
38 | |||
39 | void setHeader( const Bu::FString &sKey, const Bu::FString &sVal ); | ||
40 | void setContent( const Bu::FString &sCont ); | ||
41 | |||
42 | private: | ||
43 | int iCode; | ||
44 | Bu::FString sReason; | ||
45 | typedef Bu::Hash<Bu::FString,Bu::FString> StringHash; | ||
46 | StringHash hHeaders; | ||
47 | Bu::FString sContent; | ||
48 | }; | ||
49 | |||
50 | void sendResponse( const Response &rRes ); | ||
27 | 51 | ||
28 | private: | 52 | private: |
29 | enum TokenType | 53 | enum TokenType |
@@ -47,6 +71,9 @@ namespace Bu | |||
47 | bool isWS( char buf ); | 71 | bool isWS( char buf ); |
48 | bool isSeperator( char buf ); | 72 | bool isSeperator( char buf ); |
49 | 73 | ||
74 | void earlyResponse(); | ||
75 | void lateResponse(); | ||
76 | |||
50 | private: /* state */ | 77 | private: /* state */ |
51 | Bu::Client *pClient; | 78 | Bu::Client *pClient; |
52 | TokenList lTokens; | 79 | TokenList lTokens; |