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 | |
| 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; |
