aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2007-10-04 10:46:46 +0000
committerMike Buland <eichlan@xagasoft.com>2007-10-04 10:46:46 +0000
commit86f9fbefa58d91e151190c969216c751573bc664 (patch)
tree4e13de2666bfb063964877986dc7b8d310714483
parentb3eef5b0b82c20a9f11868ba376f6bb2d94faae4 (diff)
downloadlibbu++-86f9fbefa58d91e151190c969216c751573bc664.tar.gz
libbu++-86f9fbefa58d91e151190c969216c751573bc664.tar.bz2
libbu++-86f9fbefa58d91e151190c969216c751573bc664.tar.xz
libbu++-86f9fbefa58d91e151190c969216c751573bc664.zip
Discovered that the Bu::Client::disconnect() function didn't do anything. That
has been fixed, it now safely disconnects after emptying the Client's outgoing buffer. Added some more helpers to Bu::FString. Added the beginings of ProtocolHttp using a new method for processing protocols that's based more strongly on an NFA state machine, this makes sense, but I never had the desire to actually try implementing it before. It's working pretty well.
-rw-r--r--src/client.cpp13
-rw-r--r--src/client.h6
-rw-r--r--src/fstring.h24
-rw-r--r--src/protocolhttp.cpp163
-rw-r--r--src/protocolhttp.h63
-rw-r--r--src/server.cpp14
6 files changed, 275 insertions, 8 deletions
diff --git a/src/client.cpp b/src/client.cpp
index d416700..19d2778 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -11,7 +11,8 @@
11Bu::Client::Client( Bu::Socket *pSocket ) : 11Bu::Client::Client( Bu::Socket *pSocket ) :
12 pSocket( pSocket ), 12 pSocket( pSocket ),
13 pProto( NULL ), 13 pProto( NULL ),
14 nRBOffset( 0 ) 14 nRBOffset( 0 ),
15 bWantsDisconnect( false )
15{ 16{
16} 17}
17 18
@@ -205,5 +206,15 @@ const Bu::Socket *Bu::Client::getSocket() const
205 206
206void Bu::Client::disconnect() 207void Bu::Client::disconnect()
207{ 208{
209 bWantsDisconnect = true;
208} 210}
209 211
212bool Bu::Client::wantsDisconnect()
213{
214 return bWantsDisconnect;
215}
216
217void Bu::Client::close()
218{
219 pSocket->close();
220}
diff --git a/src/client.h b/src/client.h
index 4188a49..0b670e2 100644
--- a/src/client.h
+++ b/src/client.h
@@ -44,13 +44,12 @@ namespace Bu
44 void clearProtocol(); 44 void clearProtocol();
45 45
46 bool isOpen(); 46 bool isOpen();
47 void close();
47 48
48 const Bu::Socket *getSocket() const; 49 const Bu::Socket *getSocket() const;
49 50
50 /**
51 *@todo Make this not suck.
52 */
53 void disconnect(); 51 void disconnect();
52 bool wantsDisconnect();
54 53
55 private: 54 private:
56 Bu::Socket *pSocket; 55 Bu::Socket *pSocket;
@@ -58,6 +57,7 @@ namespace Bu
58 Bu::FString sReadBuf; 57 Bu::FString sReadBuf;
59 int nRBOffset; 58 int nRBOffset;
60 Bu::FString sWriteBuf; 59 Bu::FString sWriteBuf;
60 bool bWantsDisconnect;
61 }; 61 };
62} 62}
63 63
diff --git a/src/fstring.h b/src/fstring.h
index 9de6a56..0306aa1 100644
--- a/src/fstring.h
+++ b/src/fstring.h
@@ -605,10 +605,26 @@ namespace Bu
605 605
606 /** 606 /**
607 * Find the index of the first occurrance of (sText) 607 * Find the index of the first occurrance of (sText)
608 *@param sText (const char *) The string to search for. 608 *@param sText (const chr *) The string to search for.
609 *@returns (long) The index of the first occurrance. -1 for not found. 609 *@returns (long) The index of the first occurrance. -1 for not found.
610 */ 610 */
611 long find( const char *sText ) 611 long find( const chr cChar )
612 {
613 flatten();
614 for( long j = 0; j < pFirst->nLength; j++ )
615 {
616 if( pFirst->pData[j] == cChar )
617 return j;
618 }
619 return -1;
620 }
621
622 /**
623 * Find the index of the first occurrance of cChar
624 *@param cChar (const chr) The character to search for.
625 *@returns (long) The index of the first occurrance. -1 for not found.
626 */
627 long find( const chr *sText )
612 { 628 {
613 long nTLen = strlen( sText ); 629 long nTLen = strlen( sText );
614 flatten(); 630 flatten();
@@ -622,10 +638,10 @@ namespace Bu
622 638
623 /** 639 /**
624 * Do a reverse search for (sText) 640 * Do a reverse search for (sText)
625 *@param sText (const char *) The string to search for. 641 *@param sText (const chr *) The string to search for.
626 *@returns (long) The index of the last occurrance. -1 for not found. 642 *@returns (long) The index of the last occurrance. -1 for not found.
627 */ 643 */
628 long rfind( const char *sText ) 644 long rfind( const chr *sText )
629 { 645 {
630 long nTLen = strlen( sText ); 646 long nTLen = strlen( sText );
631 flatten(); 647 flatten();
diff --git a/src/protocolhttp.cpp b/src/protocolhttp.cpp
new file mode 100644
index 0000000..6f2ae68
--- /dev/null
+++ b/src/protocolhttp.cpp
@@ -0,0 +1,163 @@
1#include <dirent.h>
2#include <sys/wait.h>
3#include <errno.h>
4#include "protocolhttp.h"
5
6#define CRLF "\x0D\x0A"
7#define CR '\x0D'
8#define LF '\x0A'
9
10using namespace Bu;
11
12Bu::ProtocolHttp::ProtocolHttp()
13{
14}
15
16Bu::ProtocolHttp::~ProtocolHttp()
17{
18}
19
20void Bu::ProtocolHttp::onNewConnection( Bu::Client *pClient )
21{
22 this->pClient = pClient;
23
24 iState = 0;
25}
26
27#define SDB( i ) printf("state %d: %d, \"%s\"\n", i, tt, sToken.getStr() )
28
29void Bu::ProtocolHttp::onNewData( Bu::Client *pClient )
30{
31 for(;;)
32 {
33 Bu::FString sToken;
34 TokenType tt = getToken( sToken );
35
36 if( tt == ttOutOfData )
37 return;
38
39 switch( iState )
40 {
41 case 0: // Initial header
42 SDB( 0 );
43 sMethod = sToken;
44 iState = 1;
45 break;
46
47 case 1: // Misc headers
48 SDB( 1 );
49 sPath = sToken;
50 iState = 2;
51 break;
52
53 case 2: // Content
54 SDB( 2 );
55 if( strncmp( sToken.getStr(), "HTTP/", 5 ) )
56 {
57 printf("not http, disconnect.\n");
58 pClient->disconnect();
59 return;
60 }
61 else
62 {
63 char *s, *s2;
64 s = sToken.getStr()+5;
65 iMajor = strtol( s, &s2, 10 );
66 iMinor = strtol( s2+1, NULL, 10 );
67 printf("HTTP: %d.%d\n", iMajor, iMinor );
68 iState = 3;
69 }
70 break;
71
72 case 3:
73 SDB( 3 );
74 pClient->disconnect();
75 return;
76 break;
77 }
78 }
79}
80
81Bu::ProtocolHttp::TokenType Bu::ProtocolHttp::getToken( Bu::FString &line )
82{
83 char s;
84 int jmax = pClient->getInputSize();
85 bool bNonWS = false;
86
87 for( int j = 0; j < jmax; j++ )
88 {
89 pClient->peek( &s, 1, j );
90 if( iState > 2 && isSeperator( s ) )
91 {
92 if( j == 0 )
93 {
94 line += s;
95 pClient->seek( 1 );
96 return ttSeperator;
97 }
98 else
99 {
100 pClient->seek( j );
101 return ttString;
102 }
103 }
104 else if( isWS( s ) )
105 {
106 if( bNonWS )
107 {
108 pClient->seek( j );
109 return ttString;
110 }
111 }
112 else if( s == CR )
113 {
114 if( pClient->getInputSize() < 3 )
115 return ttOutOfData;
116
117 char ss[2];
118 pClient->peek( ss, 2, j+1 );
119 if( ss[0] == LF && ss[1] != ' ' && ss[1] != '\t' )
120 {
121 if( bNonWS )
122 {
123 pClient->seek( j );
124 return ttString;
125 }
126 else
127 {
128 pClient->seek( 2 );
129 return ttNewline;
130 }
131 }
132
133 j += 2;
134 if( bNonWS )
135 {
136 pClient->seek( j );
137 return ttString;
138 }
139 }
140 else
141 {
142 line += s;
143 bNonWS = true;
144 }
145 }
146
147 return ttOutOfData;
148}
149
150bool Bu::ProtocolHttp::isWS( char buf )
151{
152 return (buf == ' ' || buf == '\t');
153}
154
155bool Bu::ProtocolHttp::isSeperator( char buf )
156{
157 return (buf == '(' || buf == ')' || buf == '<' || buf == '>' ||
158 buf == '@' || buf == ',' || buf == ';' || buf == ':' ||
159 buf == '\\' || buf == '\"' || buf == '/' || buf == '[' ||
160 buf == ']' || buf == '?' || buf == '=' || buf == '{' ||
161 buf == '}' );
162}
163
diff --git a/src/protocolhttp.h b/src/protocolhttp.h
new file mode 100644
index 0000000..e10cb23
--- /dev/null
+++ b/src/protocolhttp.h
@@ -0,0 +1,63 @@
1#ifndef BU_PROTOCOL_HTTP_H
2#define BU_PROTOCOL_HTTP_H
3
4#include <stdint.h>
5#include <sys/types.h>
6
7#include "bu/protocol.h"
8#include "bu/client.h"
9#include "bu/fstring.h"
10
11namespace Bu
12{
13 /**
14 *
15 */
16 class ProtocolHttp : public Protocol
17 {
18 public: /* Types */
19 typedef Bu::List<Bu::FString> TokenList;
20
21 public: /* Interface */
22 ProtocolHttp();
23 virtual ~ProtocolHttp();
24
25 virtual void onNewConnection( Bu::Client *pClient );
26 virtual void onNewData( Bu::Client *pClient );
27
28 private:
29 enum TokenType
30 {
31 ttOutOfData,
32 ttString,
33 ttNewline,
34 ttDoubleNewline,
35 ttSeperator
36 };
37 /**
38 * Read an HTTP line, this is up to the first CRLF that isn't followed
39 * by a continuation character, converting it to one line as it reads.
40 *@param line All data read will be appended to line, even if no
41 * end-of-line is read.
42 *@returns True if an end-of-line is read and the line should be
43 * processed, false if the end-of-line has not been reached, and more
44 * data needs to be read before this operation can continue.
45 */
46 TokenType getToken( Bu::FString &line );
47 bool isWS( char buf );
48 bool isSeperator( char buf );
49
50 private: /* state */
51 Bu::Client *pClient;
52 TokenList lTokens;
53
54 int iState;
55
56 Bu::FString sMethod;
57 Bu::FString sPath;
58 int iMajor;
59 int iMinor;
60 };
61}
62
63#endif
diff --git a/src/server.cpp b/src/server.cpp
index 53b4301..29d4822 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -74,12 +74,26 @@ void Bu::Server::scan()
74 } 74 }
75 } 75 }
76 76
77 Bu::List<int> lDelete;
77 // Now we just try to write all the pending data on all the sockets. 78 // Now we just try to write all the pending data on all the sockets.
78 // this could be done better eventually, if we care about the socket 79 // this could be done better eventually, if we care about the socket
79 // wanting to accept writes (using a select). 80 // wanting to accept writes (using a select).
80 for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ ) 81 for( ClientHash::iterator i = hClients.begin(); i != hClients.end(); i++ )
81 { 82 {
82 (*i)->processOutput(); 83 (*i)->processOutput();
84 if( (*i)->wantsDisconnect() )
85 {
86 lDelete.append( i.getKey() );
87 }
88 }
89
90 for( Bu::List<int>::iterator i = lDelete.begin(); i != lDelete.end(); i++ )
91 {
92 Client *pClient = hClients.get( *i );
93 onClosedConnection( pClient );
94 pClient->close();
95 hClients.erase( *i );
96 FD_CLR( *i, &fdActive );
83 } 97 }
84} 98}
85 99