From fe1862511da4a6d66e2f11ba633c035c3cf7b14f Mon Sep 17 00:00:00 2001 From: Mike Buland Date: Mon, 8 Oct 2007 04:11:56 +0000 Subject: 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. --- src/client.cpp | 5 ++ src/client.h | 1 + src/protocolhttp.cpp | 194 ++++++++++++++++++++++++++++++++++++++++++++++++--- 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() return pSocket->isOpen(); } +void Bu::Client::write( const Bu::FString &sData ) +{ + sWriteBuf += sData; +} + void Bu::Client::write( const void *pData, int nBytes ) { 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 Bu::FString &getInput(); Bu::FString &getOutput(); + void write( const Bu::FString &sData ); void write( const void *pData, int nBytes ); void write( int8_t nData ); 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 @@ #include #include #include -#include "protocolhttp.h" +#include "bu/protocolhttp.h" +#include "bu/logger.h" #define CRLF "\x0D\x0A" #define CR '\x0D' @@ -28,6 +29,13 @@ void Bu::ProtocolHttp::onNewConnection( Bu::Client *pClient ) void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) { + logHexDump( + 1, + pClient->getInput().getStr(), + pClient->getInput().getSize(), + "input" + ); + for(;;) { Bu::FString sToken; @@ -38,19 +46,19 @@ void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) switch( iState ) { - case 0: // Initial header + case 0: // Start token, should be "method" (get, put, etc) SDB( 0 ); sMethod = sToken; iState = 1; break; - case 1: // Misc headers + case 1: // The path requested SDB( 1 ); sPath = sToken; iState = 2; break; - case 2: // Content + case 2: // The protocol name and version SDB( 2 ); if( strncmp( sToken.getStr(), "HTTP/", 5 ) ) { @@ -69,17 +77,62 @@ void Bu::ProtocolHttp::onNewData( Bu::Client *pClient ) } break; - case 3: + case 3: // End of initial header, now comes mime-style blocks. SDB( 3 ); - if( iState != ttNewline ) + if( tt == ttNewline ) + { + iState = 10; + } + else if( tt == ttDoubleNewline ) + { + earlyResponse(); + } + else { pClient->disconnect(); return; } - iState = 4; break; - case 4: + case 10: // HTTP-Message (skipped for now...) + SDB( 10 ); + if( tt == ttString ) + { + iState = 11; + } + else + { + pClient->disconnect(); + } + break; + + case 11: // Should be a colon... + SDB( 11 ); + if( tt == ttSeperator && sToken == ":" ) + { + iState = 12; + } + else + { + pClient->disconnect(); + } + break; + + case 12: + SDB( 12 ); + if( tt == ttNewline ) + { + iState = 10; + } + if( tt == ttDoubleNewline ) + { + earlyResponse(); + } + break; + + case 20: + SDB( 20 ); + printf("Content!"); break; } } @@ -118,11 +171,11 @@ Bu::ProtocolHttp::TokenType Bu::ProtocolHttp::getToken( Bu::FString &line ) } else if( s == CR ) { - if( pClient->getInputSize() < 3 ) + if( pClient->getInputSize() < 4 ) return ttOutOfData; - char ss[2]; - pClient->peek( ss, 2, j+1 ); + char ss[3]; + pClient->peek( ss, 3, j+1 ); if( ss[0] == LF && ss[1] != ' ' && ss[1] != '\t' ) { if( bNonWS ) @@ -130,6 +183,11 @@ Bu::ProtocolHttp::TokenType Bu::ProtocolHttp::getToken( Bu::FString &line ) pClient->seek( j ); return ttString; } + else if( ss[1] == CR && ss[2] == LF ) + { + pClient->seek( 4 ); + return ttDoubleNewline; + } else { pClient->seek( 2 ); @@ -168,3 +226,117 @@ bool Bu::ProtocolHttp::isSeperator( char buf ) buf == '}' ); } +void Bu::ProtocolHttp::earlyResponse() +{ + if( sMethod == "GET" ) + { + onRequest( sMethod, sPath ); + } + else + { + iState = 20; + } +} + +void Bu::ProtocolHttp::lateResponse() +{ + onRequest( sMethod, sPath ); +} + +void Bu::ProtocolHttp::sendResponse( const Response &rRes ) +{ + char buf[1024]; + int iSize = sprintf( buf, "HTTP/1.1 %d ", rRes.iCode ); + + pClient->write( buf, iSize ); + pClient->write( rRes.sReason ); + pClient->write( CRLF, 2 ); + + for( Response::StringHash::const_iterator i = rRes.hHeaders.begin(); + i != rRes.hHeaders.end(); i++ ) + { + pClient->write( i.getKey() ); + pClient->write(": ", 2 ); + pClient->write( i.getValue() ); + pClient->write( CRLF, 2 ); + } + + iSize = sprintf( buf, "Content-Length: %ld" CRLF, rRes.sContent.getSize() ); + pClient->write( buf, iSize ); + + pClient->write( CRLF, 2 ); + pClient->write( rRes.sContent ); +} + +// +// Bu::ProtocolHttp::Response +// +Bu::ProtocolHttp::Response::Response( int iCode ) : + iCode( iCode ) +{ + switch( iCode ) + { + case 100: sReason = "Continue"; break; + case 101: sReason = "Switching Protocols"; break; + case 200: sReason = "OK"; break; + case 201: sReason = "Created"; break; + case 202: sReason = "Accepted"; break; + case 203: sReason = "Non-Authoritative Information"; break; + case 204: sReason = "No Content"; break; + case 205: sReason = "Reset Content"; break; + case 206: sReason = "Partial Content"; break; + case 300: sReason = "Multiple Choices"; break; + case 301: sReason = "Moved Permanently"; break; + case 302: sReason = "Found"; break; + case 303: sReason = "See Other"; break; + case 304: sReason = "Not Modified"; break; + case 305: sReason = "Use Proxy"; break; + case 307: sReason = "Temporary Redirect"; break; + case 400: sReason = "Bad Request"; break; + case 401: sReason = "Unauthorized"; break; + case 402: sReason = "Payment Required"; break; + case 403: sReason = "Forbidden"; break; + case 404: sReason = "Not Found"; break; + case 405: sReason = "Method Not Allowed"; break; + case 406: sReason = "Not Acceptable"; break; + case 407: sReason = "Proxy Authentication Required"; break; + case 408: sReason = "Request Time-out"; break; + case 409: sReason = "Conflict"; break; + case 410: sReason = "Gone"; break; + case 411: sReason = "Length Required"; break; + case 412: sReason = "Precondition Failed"; break; + case 413: sReason = "Request Entity Too Large"; break; + case 414: sReason = "Request-URI Too Large"; break; + case 415: sReason = "Unsupported Media Type"; break; + case 416: sReason = "Requested range not satisfiable"; break; + case 417: sReason = "Expectation Failed"; break; + case 500: sReason = "Internal Server Error"; break; + case 501: sReason = "Not Implemented"; break; + case 502: sReason = "Bad Gateway"; break; + case 503: sReason = "Service Unavailable"; break; + case 504: sReason = "Gateway Time-out"; break; + case 505: sReason = "HTTP Version not supported"; break; + } +} + +Bu::ProtocolHttp::Response::Response( int iCode, const Bu::FString &sReason ) : + iCode( iCode ), + sReason( sReason ) +{ +} + +Bu::ProtocolHttp::Response::~Response() +{ +} + +void Bu::ProtocolHttp::Response::setHeader( + const Bu::FString &sKey, const Bu::FString &sVal ) +{ + hHeaders.insert( sKey, sVal ); +} + +void Bu::ProtocolHttp::Response::setContent( const Bu::FString &sCont ) +{ + sContent = sCont; +} + 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 virtual void onNewConnection( Bu::Client *pClient ); virtual void onNewData( Bu::Client *pClient ); + + virtual void onRequest( + const Bu::FString &sMethod, const Bu::FString &sPath )=0; + + class Response + { + friend class Bu::ProtocolHttp; + public: + Response( int iCode ); + Response( int iCode, const Bu::FString &sReason ); + virtual ~Response(); + + void setHeader( const Bu::FString &sKey, const Bu::FString &sVal ); + void setContent( const Bu::FString &sCont ); + + private: + int iCode; + Bu::FString sReason; + typedef Bu::Hash StringHash; + StringHash hHeaders; + Bu::FString sContent; + }; + + void sendResponse( const Response &rRes ); private: enum TokenType @@ -47,6 +71,9 @@ namespace Bu bool isWS( char buf ); bool isSeperator( char buf ); + void earlyResponse(); + void lateResponse(); + private: /* state */ Bu::Client *pClient; TokenList lTokens; -- cgit v1.2.3