// Class for managing the server's interaction with a client. #ifndef CLIENT_H_ #define CLIENT_H_ #include #include #include using namespace std; // We use the following class to manage a socket along with an // associated buffer to hold data we've read from the socket. We need // to keep the buffer around so we can deal with receiving only part // of a message on the socket followed by later receiving more of the // message. We also use this class to keep track of the file we are // sending to the client. class Client { public: int socket; // Are we currently sending this client a file? bool sending; // Does the client want the connection to remain open after // this transaction? bool persistent; // Standard Template Library string class: // the advantage is that it takes care of some of our memory // management (and has no problem with embedded \0's). string completeRequest; string incompleteBuffer; protected: // The size of sendBuffer as well as how much to try to send() // each time. static const unsigned int sendSize = 2048; // What we are sending the client in response (minus the file contents). string response; // To help keep track of the file we are sending to the client, // this buffer holds data that we've read from the file. char *sendBuffer; // How many valid bytes are in sendBuffer. unsigned int validBufferAmt; // How many unsent bytes are in sendBuffer. unsigned int unsentBufferAmt; // How many bytes are left to send in the current chunk. unsigned int chunkBytesRemaining; // Size of the file in bytes. unsigned int fileSize; // How many bytes of the file we have read in total. // Used for calculating the size of the last chunk. unsigned int readSoFar; // The file we are sending. ifstream sendFile; // The name of the file we are actually sending. string resolvedPath; // The path the client requested. string requestedPath; public: Client(int s) { socket = s; persistent = sending = false; sendBuffer = new char[sendSize]; assert(sendBuffer); validBufferAmt = unsentBufferAmt = 0; chunkBytesRemaining = 0; fileSize = 0; readSoFar = 0; } // A copy constructor to correctly create objects when they // are copied by the Standard Template Library. Client(const Client &original) { socket = original.socket; sending = original.sending; persistent = original.persistent; completeRequest = original.completeRequest; incompleteBuffer = original.incompleteBuffer; response = original.response; resolvedPath = original.resolvedPath; requestedPath = original.requestedPath; validBufferAmt = original.validBufferAmt; unsentBufferAmt = original.unsentBufferAmt; chunkBytesRemaining = original.chunkBytesRemaining; fileSize = original.fileSize; readSoFar = original.readSoFar; sendBuffer = new char[sendSize]; assert(sendBuffer); if (sending) { assert(original.sendBuffer); memcpy(sendBuffer, original.sendBuffer, validBufferAmt); sendFile.open(resolvedPath.c_str(), ios::in | ios::binary); assert(sendFile.is_open()); sendFile.seekg(readSoFar, ios_base::beg); } } // We will store these socket buffers in a vector sorted on their // socket descriptors, so we can easily find the highest-numbered // descriptor (which select() needs). The following defines // a comparison operator between two objects to determine which // is larger. The sorting algorithm will automatically use this // operator to keep the different socket buffers ordered. bool operator<(const Client &right) const { return socket < right.socket; } // Assignment operator: // This is required to work with the Standard Template Library, // which makes a copy of objects when we assign them to elements // of a vector. Client& operator=(const Client &right) { // Make sure we're not assigning to ourselves. if (this == &right) return *this; socket = right.socket; sending = right.sending; persistent = right.persistent; completeRequest = right.completeRequest; incompleteBuffer = right.incompleteBuffer; response = right.response; resolvedPath = right.resolvedPath; requestedPath = right.requestedPath; validBufferAmt = right.validBufferAmt; unsentBufferAmt = right.unsentBufferAmt; chunkBytesRemaining = right.chunkBytesRemaining; fileSize = right.fileSize; readSoFar = right.readSoFar; assert(sendBuffer); assert(right.sendBuffer); memcpy(sendBuffer, right.sendBuffer, validBufferAmt); if (sending) { sendFile.close(); sendFile.open(resolvedPath.c_str(), ios::in | ios::binary); assert(sendFile.is_open()); sendFile.seekg(readSoFar, ios_base::beg); } return *this; } ~Client() { delete [] sendBuffer; sendFile.close(); } int processRequest(); int sendFilePart(); protected: int fillSendBuffer(); int chompMethod(); int chompRequestURI(); int chompHTTPVersion(); int chompCRLF(); void appendInvalidToken(); void checkPersistent(); bool inWorkingDirectory(); string endOfResponse(); string successfulResponse(); bool resolvePath(); bool isSupportedType(); }; #endif /*CLIENT_H_*/