Web server based weather station using Sparkfun Weather Meters.

Dependencies:   FatFileSystem mbed WeatherMeters SDFileSystem

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPServer.cpp Source File

HTTPServer.cpp

00001 /* Copyright 2011 Adam Green (http://mbed.org/users/AdamGreen/)
00002 
00003    Licensed under the Apache License, Version 2.0 (the "License");
00004    you may not use this file except in compliance with the License.
00005    You may obtain a copy of the License at
00006 
00007        http://www.apache.org/licenses/LICENSE-2.0
00008 
00009    Unless required by applicable law or agreed to in writing, software
00010    distributed under the License is distributed on an "AS IS" BASIS,
00011    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012    See the License for the specific language governing permissions and
00013    limitations under the License.
00014 */
00015 /* Implementation of HTTP Server functionality. */
00016 #include "HTTPServer.h"
00017 #include "network.h"
00018 #include "debug.h"
00019 
00020 
00021 // HTTP_TRACE to 1/0 to enable/disable logging for the HTTP server.
00022 #define HTTP_TRACE      0
00023 #if HTTP_TRACE
00024     #define TRACE printf
00025 #else
00026     static void __trace(...)
00027     {
00028         return;
00029     }
00030     #define TRACE __trace
00031 #endif // HTTP_TRACE
00032 
00033 
00034 
00035 // Maximum size of buffer to be allocated on the stack for building filename
00036 // from URI that can be used in mbed fopen() call.
00037 #define HTTP_MAX_FILENAME       64
00038 
00039 // Maximum size of a request line to be buffered.
00040 #define HTTP_MAX_REQUEST_LINE   128
00041 
00042 
00043 // Utility macro to determine the element count of an array.
00044 #define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
00045 
00046 
00047 
00048 // Default filenames to be used when the requested URI is "/"
00049 static const char* g_DefaultFilenames[] =
00050 {
00051     "/index.html",
00052     "/index.htm"
00053 };
00054 
00055 
00056 
00057 // Static-Scope Function Prototypes.
00058 static void        NetworkPrintRemoteAddress(tcp_pcb* pPCB);
00059 
00060 
00061 
00062 // Structure used to represent each header to be sent.  It includes both a
00063 // pointer to the constant string and the length of the constant string so
00064 // that a memcpy() can be used for moving into buffer instead of strcpy().
00065 struct SHeader
00066 {
00067     // Header string.
00068     const char*     pHeaderString;
00069     // Length of header string, not including the NULL terminator.
00070     unsigned int    HeaderLength;
00071 };
00072 
00073 
00074 // Structure which stores information about each HTTP client connection.
00075 class CHTTPContext : protected IHTTPRequestHandlerContext
00076 {
00077 public:
00078     // Constructors/Destructors
00079     CHTTPContext(CHTTPServer* pHTTPServer, tcp_pcb* pPCB);
00080     ~CHTTPContext();
00081     
00082     // IHTTPRequestHandlerContext methods.
00083     virtual int  BeginRequestHeaders();
00084     virtual void WriteRequestHeader(const char* pHeaderName, 
00085                                     const char* pHeaderValue);
00086     virtual void EndRequestHeaders();
00087     virtual int BeginRequestContent(size_t ContentSize);
00088     virtual void WriteRequestContentBlock(const void* pBlockBuffer, 
00089                                           size_t BlockBufferSize);
00090     virtual void EndRequestContent();
00091     virtual const char* GetStatusLine(size_t* pStatusLineLength);
00092     virtual const char* GetResponseHeaders(size_t* pResponseHeaderLength);
00093     virtual int BeginResponseContent();
00094     virtual size_t ReadResponseContentBlock(char*  pBuffer,
00095                                             size_t BytesToRead);
00096     virtual void EndResponseContent();
00097     virtual void Release();
00098     
00099     CHTTPServer*    Server() { return m_pHTTPServer; }
00100     CHTTPContext*   GetNext() { return m_pNext; }
00101     void            RemovedFromWaitingList(CHTTPContext* pPrev) 
00102                     { 
00103                         if (pPrev)
00104                         {
00105                             pPrev->m_pNext = this->m_pNext;
00106                         }
00107                         m_pNext = NULL; 
00108                         m_WaitingForBuffer = 0; 
00109                     }
00110     void            AddToWaitingList(CHTTPContext* pPrev)
00111                     {
00112                         if (pPrev)
00113                         {
00114                             assert ( !pPrev->m_pNext );
00115                             pPrev->m_pNext = this;
00116                         }
00117                         m_WaitingForBuffer = 1;
00118                     }
00119     int             IsWaitingForBuffer() { return m_WaitingForBuffer; }
00120 
00121     void            CloseConnection();
00122     void            FreeContext(int ForcedFree);
00123     void            BufferAvailable();
00124 
00125 protected:
00126     // Static methods for lwIP callbacks.
00127     static err_t        HTTPRecv(void* pvArg, tcp_pcb* pPCB, pbuf* pBuf, err_t err);
00128     static err_t        HTTPSent(void* pvArg, tcp_pcb* pPCB, u16_t SendCount);
00129     static void         HTTPError(void* pvArg, err_t Error);
00130     static err_t        HTTPPoll(void* pvArg, tcp_pcb* pPCB);
00131     
00132     // Protected helper methods.
00133     void                ProcessRequestData(pbuf* pBuf);
00134     void                BufferRequestLines(pbuf* pBuf);
00135     void                ProcessRequestContent(pbuf* pBuf);
00136     void                ParseRequestLine();
00137     void                ParseFirstRequestLine();
00138     void                ParseHeaderRequestLine();
00139     void                StartRequestHandler(const char* pURI);
00140     void                StartGetRequest(const char* pURI);
00141     void                StartHeadRequest(const char* pURI);
00142     void                StartPostRequest(const char* pURI);
00143     void                StartBadRequest(const char* pRequest);
00144     void                OpenFile(const char* pURI);
00145     void                PrepareResponse();
00146     void                PrepareResponseHeaders();
00147     void                ReadContent();
00148     void                SendResponse();
00149     int                 WriteData();
00150 
00151     // States used to track the HTTP request parsing progress.
00152     enum EParseState {
00153         STATE_FIRST_LINE,
00154         STATE_HEADERS,
00155         STATE_CONTENT,
00156         STATE_DONE
00157     };
00158     
00159     // Type of HTTP request that has been made by the client.
00160     enum ERequestType
00161     {
00162         TYPE_GET_0_9,
00163         TYPE_GET_1_0,
00164         TYPE_HEAD,
00165         TYPE_POST,
00166         TYPE_BAD_REQUEST
00167     };
00168 
00169     // Pointer to parent HTTP Server object.
00170     CHTTPServer*    m_pHTTPServer;
00171     // Pointer to the tcp_pcb 'socket' for this context.
00172     tcp_pcb*        m_pPCB;
00173     // The next context in the buffer waiting queue.
00174     CHTTPContext*   m_pNext;
00175     // Handle of file to be sent to the client.
00176     FILE*           m_pFile;
00177     // Pointer to the request handler context.  May point to self for
00178     // default GET/HEAD request handling.
00179     IHTTPRequestHandlerContext* m_pRequestHandlerContext;
00180     // Content-Type description header field.
00181     const char*     m_pContentType;
00182     // HTML text to be sent back in response to error.
00183     const char*     m_pErrorHTML;
00184     // Queue of buffers used by this context.  BufferQueueHead and 
00185     // BufferQueueTail track the current buffers being written and/or
00186     // waiting for acknowledgment from remote machine.
00187     SBuffer*        m_BufferQueue[2];
00188     // Array of pointers to constant header strings to be sent before response
00189     // content.  Status Line, Server:, Other Headers, /r/n
00190     SHeader         m_HeaderArray[4];
00191     // The length of the m_pContentType string.
00192     size_t          m_ContentTypeLength;
00193     // The length of the m_pErrorHTML string.
00194     size_t          m_ErrorHTMLLength;
00195     // The current read offset into the m_pErrorHTML string.
00196     size_t          m_ErrorHTMLOffset;
00197     // UNDONE: Many of these fields could be packed into a single field to save memory.
00198     // Are we waiting for a buffer already?
00199     int             m_WaitingForBuffer;
00200     // Are we in the process of closing down the connection?
00201     int             m_ClosingConnection;
00202     // Should request headers be sent to handler?
00203     int             m_SendRequestHeadersToHandler;
00204     // Should request content be sent to handler?
00205     int             m_SendRequestContentToHandler;
00206     // Content-Length of request content body.
00207     unsigned int    m_RequestContentLength;
00208     // The current state of the request parsing code.
00209     EParseState     m_ParseState;
00210     // The type of request being made by the client.
00211     ERequestType    m_RequestType;
00212     // UNDONE: Check for memory savings across these structures.
00213     // Offset into m_LineBuffer where next character should be placed.
00214     unsigned short  m_LineBufferOffset;
00215     // Offset into current header being sent to client.
00216     unsigned short  m_HeaderOffset;
00217     // Head and tail indices for SentBufferQueue.
00218     // Head is the buffer currently being written.
00219     unsigned char   m_BufferQueueHead;
00220     // Tail is the buffer currently in the process of being acknowledged.
00221     unsigned char   m_BufferQueueTail;
00222     // Number of tcp_write() retries attempted so far.
00223     unsigned char   m_RetryCount;
00224     // Current index into HeaderArray[] of next header to be sent to client.
00225     unsigned char   m_HeaderIndex;
00226     // Each line of the request is buffered here.
00227     char            m_LineBuffer[HTTP_MAX_REQUEST_LINE+1];
00228 };
00229 
00230 
00231 CHTTPContext::CHTTPContext(CHTTPServer* pHTTPServer, tcp_pcb* pPCB)
00232 {
00233     m_pHTTPServer = pHTTPServer;
00234     m_pPCB = pPCB;
00235     m_pNext = NULL;
00236     m_pFile = NULL;
00237     m_pRequestHandlerContext = NULL;
00238     m_pContentType = NULL;
00239     m_pErrorHTML = NULL;
00240     memset(m_BufferQueue, 0, sizeof(m_BufferQueue));
00241     memset(m_HeaderArray, 0, sizeof(m_HeaderArray));
00242     m_ContentTypeLength = 0;
00243     m_ErrorHTMLLength = 0;
00244     m_ErrorHTMLOffset = 0;
00245     m_WaitingForBuffer = 0;
00246     m_ClosingConnection = 0;
00247     m_SendRequestHeadersToHandler = 0;
00248     m_SendRequestContentToHandler = 0;
00249     m_RequestContentLength = 0;
00250     m_ParseState = STATE_FIRST_LINE;
00251     m_RequestType = TYPE_BAD_REQUEST;
00252     m_LineBufferOffset = 0;
00253     m_HeaderOffset = 0;
00254     m_BufferQueueHead = 0;
00255     m_BufferQueueTail = 0;
00256     m_RetryCount = 0;
00257     m_HeaderIndex = 0;
00258     m_LineBuffer[0] = '\0';
00259 
00260     // Associate this context with the lwIP PCB object.
00261     tcp_arg(pPCB, this);
00262     
00263     // Setup the callbacks to be called whenever any activity occurs on this new
00264     // client PCB.
00265     tcp_recv(pPCB, HTTPRecv);
00266     tcp_sent(pPCB, HTTPSent);
00267     tcp_err(pPCB, HTTPError);
00268     
00269     // There might be scenarios where the application can't queue up more work
00270     // for this client PCB (due to things like out of memory) so that activity
00271     // could cease on this PCB when there were no more calls to functions like
00272     // HTTPSent().  To make sure this doesn't happen, have lwIP callback into 
00273     // the HTTPPoll() function every 4 iterations of the coarse TCP timer (2 
00274     // seconds).  HTTPPoll() can take care of retrying any previously failed 
00275     // operations.
00276     tcp_poll(pPCB, HTTPPoll, 4);
00277 }
00278 
00279 
00280 // Destructor
00281 CHTTPContext::~CHTTPContext()
00282 {
00283     // Make sure that everything was cleaned up before destructor was called.
00284     assert ( NULL == m_pFile );
00285     assert ( NULL == m_pPCB );
00286     assert ( NULL == m_pRequestHandlerContext );
00287 }
00288 
00289 
00290 int  CHTTPContext::BeginRequestHeaders()
00291 {
00292     // This context already parses out the Content-Length header that it needs.
00293     return 0;
00294 }
00295 
00296 void CHTTPContext::WriteRequestHeader(const char* pHeaderName, 
00297                                       const char* pHeaderValue)
00298 {
00299     static const int ShouldNotBeCalled = 0;
00300     
00301     (void)pHeaderName;
00302     (void)pHeaderValue;
00303     
00304     assert ( ShouldNotBeCalled );
00305 }
00306 
00307 void CHTTPContext::EndRequestHeaders()
00308 {
00309     static const int ShouldNotBeCalled = 0;
00310     
00311     assert ( ShouldNotBeCalled );
00312 }
00313 
00314 int CHTTPContext::BeginRequestContent(size_t ContentSize)
00315 {
00316     (void)ContentSize;
00317     
00318     // Only processing GET and HEAD requests so don't care about content sent
00319     // in request.
00320     return 0;
00321 }
00322 
00323 void CHTTPContext::WriteRequestContentBlock(const void* pBlockBuffer, 
00324                                             size_t BlockBufferSize)
00325 {
00326     static const int ShouldNotBeCalled = 0;
00327     
00328     (void)pBlockBuffer;
00329     (void)BlockBufferSize;
00330     
00331     assert ( ShouldNotBeCalled );
00332 }
00333 
00334 void CHTTPContext::EndRequestContent()
00335 {
00336     static const int ShouldNotBeCalled = 0;
00337     
00338     assert ( ShouldNotBeCalled );
00339 }
00340 
00341 const char* CHTTPContext::GetStatusLine(size_t* pStatusLineLength)
00342 {
00343     static const char Ok[] = "HTTP/1.0 200 OK\r\n";
00344     static const char NotFound[] = "HTTP/1.0 404 Not Found\r\n";
00345     static const char BadRequest[] = "HTTP/1.0 400 Bad Request\r\n";
00346     static const char NotImplemented[] = "HTTP/1.0 501 Not Implemented\r\n";
00347     
00348     switch (m_RequestType)
00349     {
00350     case TYPE_HEAD:
00351     case TYPE_GET_1_0:
00352         if (m_pFile)
00353         {
00354             *pStatusLineLength = sizeof(Ok) - 1;
00355             return Ok;
00356         }
00357         else
00358         {
00359             *pStatusLineLength = sizeof(NotFound) - 1;
00360             return NotFound;
00361         }
00362         break;
00363     case TYPE_GET_0_9:
00364         // Shouldn't get called for such a request.
00365         assert ( TYPE_GET_0_9 != m_RequestType );
00366         return NULL;
00367     case TYPE_BAD_REQUEST:
00368         *pStatusLineLength = sizeof(BadRequest) - 1;
00369         return BadRequest;
00370     default:
00371         // This server has no default handling for any other type of request.
00372         *pStatusLineLength = sizeof(NotImplemented) - 1;
00373         return NotImplemented;
00374     }
00375 }
00376 
00377 const char* CHTTPContext::GetResponseHeaders(size_t* pResponseHeaderLength)
00378 {
00379     // Additional error text will be returned as HTML text.
00380     static const char ContentTypeHTML[] = "Content-type: text/html\r\n";
00381     static const char NotImplementedHTML[] = "<html>"
00382                                              "<body><h2>501: Not Implemented.</h2></body>"
00383                                              "</html>\r\n";
00384     static const char BadRequestHTML[] = "<html>"
00385                                          "<body><h2>400: Bad Request.</h2></body>"
00386                                          "</html>\r\n";
00387  
00388     switch (m_RequestType)
00389     {
00390     case TYPE_HEAD:
00391     case TYPE_GET_1_0:
00392         *pResponseHeaderLength = m_ContentTypeLength;
00393         return m_pContentType;
00394     case TYPE_GET_0_9:
00395         // Shouldn't get called for such a request.
00396         assert ( TYPE_GET_0_9 != m_RequestType );
00397         return NULL;
00398     case TYPE_BAD_REQUEST:
00399         // Will send error back as HTML text.
00400         m_pErrorHTML = BadRequestHTML;
00401         m_ErrorHTMLLength = sizeof(BadRequestHTML) - 1;
00402         *pResponseHeaderLength = sizeof(ContentTypeHTML) - 1;
00403         return ContentTypeHTML;
00404     default:
00405         // This server has no default handling for any other type of request.
00406         m_pErrorHTML = NotImplementedHTML;
00407         m_ErrorHTMLLength = sizeof(NotImplementedHTML) - 1;
00408         *pResponseHeaderLength = sizeof(ContentTypeHTML) - 1;
00409         return ContentTypeHTML;
00410     }
00411 }
00412 
00413 int CHTTPContext::BeginResponseContent()
00414 {
00415     // Have response content to send back so return 1;
00416     assert ( m_pFile || m_pErrorHTML );
00417     return 1;
00418 }
00419 
00420 size_t CHTTPContext::ReadResponseContentBlock(char*  pBuffer,
00421                                               size_t BytesToRead)
00422 {
00423     if (m_pFile)
00424     {
00425         // Read content from file.
00426         return fread(pBuffer, 1, BytesToRead, m_pFile);
00427     }
00428     else
00429     {
00430         // Read content from HTML text for error.
00431         size_t BytesLeft = m_ErrorHTMLLength - m_ErrorHTMLOffset;
00432         
00433         if (BytesToRead > BytesLeft)
00434         {
00435             BytesToRead = BytesLeft;
00436         }
00437         memcpy(pBuffer, &m_pErrorHTML[m_ErrorHTMLOffset], BytesToRead);
00438         m_ErrorHTMLOffset += BytesToRead;
00439 
00440         return BytesToRead;
00441     }
00442 }
00443 
00444 void CHTTPContext::EndResponseContent()
00445 {
00446     if (m_pFile)
00447     {
00448         fclose(m_pFile);
00449         m_pFile = NULL;
00450     }
00451 }
00452 
00453 void CHTTPContext::Release()
00454 {
00455     // Just make sure that any files are closed.
00456     EndResponseContent();
00457 
00458     return;
00459 }
00460 
00461 
00462 /* Called by lwIP whenever data is sent to an active connection.  The main task
00463    of this callback is to acknowledge application receipt of the data, parse 
00464    the received data to determine which file is being requested by the client,
00465    open the requested file, and start sending it back to the remote client. 
00466    
00467    Parameters:
00468     pvArg is the value set on the active PCB through the call to tcp_arg in
00469         HTTPAccept() which is a pointer to the HTTP context for this
00470         connection.
00471     pPCB is a pointer to the PCB that has just received some data from the 
00472         client.
00473     pBuf is a pointer to a PBUF which contains a linked list of the data
00474         received from the remote client.
00475     err is passed in from the lwIP stack to indicate if an error occurred.
00476     
00477    Returns:
00478     ERR_ABRT if we end up aborting the connection and ERR_OK otherwise.
00479 */
00480 err_t CHTTPContext::HTTPRecv(void* pvArg, tcp_pcb* pPCB, pbuf* pBuf, err_t err)
00481 {
00482     CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg;
00483     
00484     // Validate parameters.
00485     assert ( pPCB );
00486     assert ( ERR_OK == err );
00487     
00488     // If the client has closed their end then pBuf will be NULL so the server
00489     // should shutdown its end as well.
00490     if (!pBuf)
00491     {
00492         TRACE("HTTP: Closing as client closed its end for connection: ");
00493         NetworkPrintRemoteAddress(pPCB);
00494         TRACE("\r\n");
00495         
00496         pHTTPContext->CloseConnection();
00497         pHTTPContext->FreeContext(TRUE);
00498 
00499         return ERR_OK;
00500     }
00501         
00502     // Let lwIP know that the HTTP application has received the data.
00503     tcp_recved(pPCB, pBuf->tot_len);
00504 
00505     // Just return if we have already freed the client context from a close
00506     // attempt.
00507     if (!pvArg)
00508     {
00509         TRACE("HTTP: Ignoring HTTPRecv() as client context was already freed for: ");
00510         NetworkPrintRemoteAddress(pPCB);
00511         TRACE("\r\n");
00512         goto Error;
00513     }
00514 
00515     // The PCB shouldn't change out underneath this object!
00516     assert ( pPCB == pHTTPContext->m_pPCB );
00517     
00518     pHTTPContext->ProcessRequestData(pBuf);
00519 
00520 Error:
00521     // Release the pbuf as the application no longer requires any of its data
00522     pbuf_free(pBuf);
00523     
00524     return ERR_OK;
00525 }
00526 
00527 
00528 /* Called by lwIP whenever sent data is acknowledged by the remote machine.  
00529    The main task of this callback is to update the byte count of unacknowledged
00530    bytes and send more data once the previously sent data has been acknowledged.
00531    
00532    Parameters:
00533     pvArg is the value set on the active PCB through the call to tcp_arg in
00534         HTTPAccept() which is a pointer to the HTTP context for this
00535         connection.
00536     pPCB is a pointer to the PCB that has just received acknowledgement from
00537         the remote client of sent data.
00538     AcknowledgeCount is the number of bytes that the remote client has
00539         acknowledged receipt of.
00540     
00541    Returns:
00542     ERR_ABRT if we end up aborting the connection and ERR_OK otherwise.
00543 */
00544 err_t CHTTPContext::HTTPSent(void* pvArg, tcp_pcb* pPCB, u16_t AcknowledgeCount)
00545 {
00546     CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg;
00547 
00548     // Validate parameters.
00549     assert ( pvArg );
00550     assert ( pPCB );
00551     assert ( AcknowledgeCount );
00552 
00553     // Loop through all of the buffers in the sent state to update their
00554     // unacknowledged count since this acknowledgment can span buffers.
00555     while (AcknowledgeCount > 0)
00556     {
00557         SBuffer*      pSentBuffer = pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail];
00558         
00559         // We should have a buffer in the sent state if we are getting back
00560         // acknowledgments from the remote machine.
00561         assert ( pSentBuffer );
00562         
00563         // Update outstanding byte count for this buffer.
00564         if (AcknowledgeCount >= pSentBuffer->UnacknowledgedBytes)
00565         {
00566             // This callback acknowledges all of the data in the buffer at the
00567             // tail of the queue and possibly spans into the next buffer as
00568             // well.  It is also possible that there is still more data to be
00569             // written from this buffer.
00570             AcknowledgeCount -= pSentBuffer->UnacknowledgedBytes;
00571             pSentBuffer->UnacknowledgedBytes = 0;
00572         
00573             if (0 == pSentBuffer->BytesToWrite)
00574             {
00575                 // Free the buffer which might cause another client context to be
00576                 // invoked if it was waiting for a buffer.
00577                 pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail] = NULL;
00578                 pHTTPContext->m_BufferQueueTail = (pHTTPContext->m_BufferQueueTail + 1) & 1;
00579                 pHTTPContext->m_pHTTPServer->FreeBuffer(pSentBuffer);
00580                 
00581                 // Check to see if this context is in the closing state and all sent
00582                 // buffers are now free.
00583                 if (pHTTPContext->m_ClosingConnection && 
00584                     !pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail])
00585                 {
00586                     // Free the context and return immediately since the context is
00587                     // no longer valid.
00588                     assert ( !pHTTPContext->m_BufferQueue[0] );
00589                     assert ( !pHTTPContext->m_BufferQueue[1] );
00590                     assert ( !pHTTPContext->m_WaitingForBuffer );
00591                     
00592                     pHTTPContext->FreeContext(FALSE);
00593                     return ERR_OK;
00594                 }
00595             }
00596             else
00597             {
00598                 // We still have data to write from this buffer so there should
00599                 // no more bytes to acknowledge in another buffer.
00600                 assert ( 0 == AcknowledgeCount );
00601             }
00602         }
00603         else
00604         {
00605             // This callback is just acknowledging a portion of the data in
00606             // this buffer.
00607             pSentBuffer->UnacknowledgedBytes -= AcknowledgeCount;
00608             AcknowledgeCount = 0;
00609         }
00610     }
00611     
00612     // Attempt to send more response data to this client.
00613     pHTTPContext->SendResponse();
00614     
00615     return ERR_OK;
00616 }
00617 
00618 
00619 /* Called by lwIP every 2 seconds (4 iterations of the tcp_slowtmr) as 
00620   specified in the tcp_poll() call in HTTPInit().  Can be used to retry
00621   sending of response data or closing of the PCB.
00622       
00623    Parameters:
00624     pvArg is the value set on the active PCB through the call to tcp_arg in
00625         HTTPAccept() which is a pointer to the HTTP context for this
00626         connection.  It will be NULL if we have previously failed to close
00627         the PCB.
00628     pPCB is a pointer to the low level lwIP API socket object for this
00629         connection.
00630     
00631    Returns:
00632     ERR_ABRT if we end up aborting the connection and ERR_OK otherwise.
00633 */
00634 err_t CHTTPContext::HTTPPoll(void* pvArg, tcp_pcb* pPCB)
00635 {
00636     CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg;
00637     
00638     // Validate parameters.
00639     assert ( pPCB );
00640     
00641     // Check to see if we need to retry closing the PCB.
00642     if (!pHTTPContext)
00643     {
00644         err_t CloseResult;
00645 
00646         TRACE("HTTP: Retrying close for connection to: ");
00647         NetworkPrintRemoteAddress(pPCB);
00648         TRACE("\r\n");
00649 
00650         CloseResult = tcp_close(pPCB);
00651         if (ERR_OK != CloseResult)
00652         {
00653             // This is the second failed attempt to close the PCB, force an
00654             // abort.
00655             tcp_abort(pPCB);
00656             return ERR_ABRT;
00657         }
00658         else
00659         {
00660             return ERR_OK;
00661         }
00662     }
00663     
00664     // Close the connection if the client hasn't sent its request within the polling interval.
00665     if (STATE_DONE != pHTTPContext->m_ParseState)
00666     {
00667         TRACE("HTTP: Closing connection due to no HTTP request from connection to: ");
00668         NetworkPrintRemoteAddress(pPCB);
00669         TRACE("\r\n");
00670         
00671         pHTTPContext->CloseConnection();
00672         pHTTPContext->FreeContext(TRUE);
00673         
00674         return ERR_OK;
00675     }
00676 
00677     // If we have unacknowledged bytes outstanding then we will still
00678     // receive a HTTPSent() callback from lwIP so no need to retry data sends
00679     // from here.
00680     // NOTE: This version will wait forever for a request from the client.
00681     if (!pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail] && 
00682          pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueHead])
00683     {
00684         if (pHTTPContext->m_RetryCount++ > 8)
00685         {
00686             // We have already retried sending the data 8 times, so close
00687             // the connection.
00688             TRACE("HTTP: Failed multiple retries to send data for connection to: ");
00689             NetworkPrintRemoteAddress(pPCB);
00690             TRACE("\r\n");
00691 
00692             pHTTPContext->CloseConnection();
00693         }
00694         else
00695         {
00696             // Retry sending the response data again.
00697             TRACE("HTTP: Retrying to send data for connection to: ");
00698             NetworkPrintRemoteAddress(pPCB);
00699             TRACE("\r\n");
00700             
00701             pHTTPContext->SendResponse();
00702         }
00703     }
00704 
00705     return ERR_OK;
00706 }
00707 
00708 
00709 /* Called by lwIP whenever the PCB is closed or reset by the remote client.
00710    Gives the application the opportunity to free up the context object.
00711       
00712    Parameters:
00713     pvArg is the value set on the active PCB through the call to tcp_arg in
00714         HTTPAccept() which is a pointer to the HTTP context for this
00715         connection.
00716     Error is the error that caused the connection to be shutdown.  It can be
00717         set to ERR_ABRT or ERR_RST.
00718     
00719    Returns:
00720     Nothing.
00721 */
00722 void CHTTPContext::HTTPError(void* pvArg, err_t Error)
00723 {
00724     CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg;
00725     
00726     // Unused parameters.
00727     (void)Error;
00728     
00729     TRACE("HTTP: Forcing connection context to be freed.\r\n");
00730     
00731     // Free the context if it is non-NULL.
00732     if (pHTTPContext)
00733     {
00734         pHTTPContext->FreeContext(TRUE);
00735     }
00736 }
00737 
00738 
00739 /* Attempts to close the PCB connection.  The context is placed into a closing
00740    state where it waits for the data written to be acknowledged by the remote
00741    machine before freeing up its memory.
00742    
00743    Parameters:
00744     None.
00745    Returns:
00746     Nothing.
00747 */
00748 void CHTTPContext::CloseConnection()
00749 {
00750     err_t CloseResult = ERR_MEM;
00751     
00752     // Validate parameters.
00753     assert ( m_pPCB );
00754     
00755     TRACE("HTTP: Closing connection to: ");
00756     NetworkPrintRemoteAddress(m_pPCB);
00757     TRACE("\r\n");
00758     
00759     // Flag the context as being in the closing state.
00760     m_ClosingConnection = 1;
00761     
00762     // Have no more data to send so no need for poll callback unless the 
00763     // tcp_close() below should fail and require retrying.
00764     tcp_poll(m_pPCB, NULL, 0);
00765 
00766     // Atempt to close the connection.
00767     CloseResult = tcp_close(m_pPCB);
00768     if (ERR_OK != CloseResult)
00769     {
00770         // Close wasn't successful so re-enable the HTTPPoll() callback.
00771         tcp_poll(m_pPCB, HTTPPoll, 4);
00772     }
00773 }
00774 
00775 
00776 /* Frees up the resources used by the context.
00777    
00778    Parameters:
00779     ForcedFree indicates whether a shutdown is being forced and therefore it is
00780         OK to free the buffers even though they might indicate they still have
00781         outstanding data.
00782         
00783    Returns:
00784     Nothing.
00785 */
00786 void CHTTPContext::FreeContext(int ForcedFree)
00787 {
00788     size_t i;
00789     
00790     if (m_pPCB)
00791     {
00792         TRACE("HTTP: Freeing context for connection to: ");
00793         NetworkPrintRemoteAddress(m_pPCB);
00794         TRACE("\r\n");
00795     }
00796     else
00797     {
00798         TRACE("HTTP: Freeing connection due to unexpected close.\r\n");
00799     }
00800     
00801     // Free the HTTP context record and any resources it owned.
00802     if (m_WaitingForBuffer)
00803     {
00804         m_pHTTPServer->RemoveWaitingContext(this);
00805     }
00806     for (i = 0 ; i < ARRAYSIZE(m_BufferQueue) ; i++)
00807     {
00808         SBuffer* pBuffer = m_BufferQueue[i];
00809         if (pBuffer)
00810         {
00811             if (ForcedFree)
00812             {
00813                 // 0 out the counts if we are forcing a free.
00814                 pBuffer->BytesToWrite = 0;
00815                 pBuffer->UnacknowledgedBytes = 0;
00816             }
00817             
00818             // Free the buffer.
00819             m_pHTTPServer->FreeBuffer(m_BufferQueue[i]);
00820             m_BufferQueue[i] = NULL;
00821         }
00822     }
00823     if (m_pRequestHandlerContext)
00824     {
00825         m_pRequestHandlerContext->Release();
00826         m_pRequestHandlerContext = NULL;
00827     }
00828 
00829     // Clear the callbacks for the PCB as we are getting ready to close it.
00830     if (m_pPCB)
00831     {
00832         // Keep the polling callback enabled incase the previous tcp_close()
00833         // attempt failed.
00834         tcp_arg(m_pPCB, NULL);
00835         tcp_accept(m_pPCB, NULL);
00836         tcp_recv(m_pPCB, NULL);
00837         tcp_sent(m_pPCB, NULL);
00838         tcp_err(m_pPCB, NULL);
00839         m_pPCB = NULL;
00840     }
00841 
00842     delete this;
00843 }
00844 
00845 
00846 /* Processes the HTTP request data as it comes in from the client.
00847 
00848    Parameters:
00849     pBuf points to the buffer of inbound TCP/IP data from the connection.
00850     
00851    Returns:
00852     Nothing.
00853 */
00854 void CHTTPContext::ProcessRequestData(pbuf* pBuf)
00855 {
00856     // Validate parameters.
00857     assert ( pBuf );
00858     
00859     switch (m_ParseState)
00860     {
00861     case STATE_FIRST_LINE:
00862     case STATE_HEADERS:
00863         BufferRequestLines(pBuf);
00864         break;
00865     case STATE_CONTENT:
00866         ProcessRequestContent(pBuf);
00867         break;
00868     case STATE_DONE:
00869         // We should have received everything already so discard data and
00870         // return.
00871         return;
00872     }
00873 
00874     if (STATE_DONE == m_ParseState)
00875     {
00876         // Let the application interface know that we are done sending it
00877         // the request content.
00878         if (m_SendRequestContentToHandler)
00879         {
00880             m_pRequestHandlerContext->EndRequestContent();
00881         }
00882 
00883         // And prepare response headers and content to send back to client.
00884         PrepareResponse();
00885 
00886         // Attempt to allocate a buffer and fill it in with header and data
00887         // content.
00888         ReadContent();
00889 
00890         // Attempt to send first segments of response data.
00891         SendResponse();
00892     }
00893 }
00894 
00895 
00896 /* Buffers up the HTTP requests a line at a time and then processes it once
00897    a complete line has been buffered.
00898 
00899    Parameters:
00900     pBuf points to the buffer of inbound TCP/IP data from the connection.
00901     
00902    Returns:
00903     Nothing.
00904 */
00905 void CHTTPContext::BufferRequestLines(pbuf* pBuf)
00906 {
00907     // Validate parameters.
00908     assert ( pBuf );
00909     
00910     // Loop through all chunks in the pbuf list and parse its content a line
00911     // at a time.
00912     while (pBuf)
00913     {
00914         char* pCurr = (char*)pBuf->payload;
00915         u16_t BytesLeft = pBuf->len;
00916         
00917         // Loop through the characters in this chunk.
00918         while (BytesLeft)
00919         {
00920             char CurrChar = *pCurr;
00921             
00922             if ('\n' == CurrChar)
00923             {
00924                 // Have encountered end of line.
00925                 m_LineBuffer[m_LineBufferOffset] = '\0';
00926                 
00927                 ParseRequestLine();
00928 
00929                 // Reset pointer to start buffering next line.
00930                 m_LineBufferOffset = 0;
00931             }
00932             else if ('\r' != CurrChar &&
00933                      m_LineBufferOffset < ARRAYSIZE(m_LineBuffer)-1)
00934             {
00935                 // Copy character into line buffer.  Above conditional protects
00936                 // from buffer overflow on the write and leaves room for NULL
00937                 // terminator.
00938                 m_LineBuffer[m_LineBufferOffset++] = CurrChar;
00939             }
00940             
00941             // Advance to the next character in this chunk.
00942             pCurr++;
00943             BytesLeft--;
00944         }
00945         
00946         // Advance to the next chunk in the pbuf list.
00947         pBuf = pBuf->next;
00948     }
00949 }
00950 
00951 
00952 /* Processes the HTTP request content data as it comes in from the client.
00953 
00954    Parameters:
00955     pBuf points to the buffer of inbound TCP/IP data from the connection.
00956     
00957    Returns:
00958     Nothing.
00959 */
00960 void CHTTPContext::ProcessRequestContent(pbuf* pBuf)
00961 {
00962     // Validate parameters.
00963     assert ( pBuf );
00964 
00965     while (pBuf && m_RequestContentLength > 0);
00966     {
00967         size_t BytesToWrite = pBuf->len;
00968         if (BytesToWrite > m_RequestContentLength)
00969         {
00970             // Make sure that we don't send the application interface more
00971             // content than we told it we would send.
00972             BytesToWrite = m_RequestContentLength;
00973         }
00974         if (m_SendRequestContentToHandler)
00975         {
00976             // Only send the application interface the request content if
00977             // it has indicated that it wants to receive the data.
00978             m_pRequestHandlerContext->WriteRequestContentBlock(pBuf->payload, BytesToWrite);
00979         }
00980         m_RequestContentLength -= BytesToWrite;
00981         
00982         // Advance to next chunk in the pbuf list.
00983         pBuf = pBuf->next;
00984     }
00985 
00986     if (0 == m_RequestContentLength)
00987     {
00988         // Have finished receiving the request content so advance to the next
00989         // state where response data is sent to the client.
00990         m_ParseState = STATE_DONE;
00991     }
00992 }
00993 
00994 
00995 /* Attempts to parse the current line in m_LineBuffer.
00996 
00997    Parameters:
00998     None.
00999     
01000    Returns:
01001     Nothing.
01002 */
01003 void CHTTPContext::ParseRequestLine()
01004 {
01005     switch(m_ParseState)
01006     {
01007     case STATE_FIRST_LINE:
01008         ParseFirstRequestLine();
01009         break;
01010     case STATE_HEADERS:
01011         ParseHeaderRequestLine();
01012         break;
01013     default:
01014         assert ( FALSE );
01015         break;
01016     }
01017 }
01018 
01019 
01020 /* Attempts to parse the first request line found in m_LineBuffer which should
01021    contain the request method, URI, etc.
01022 
01023    Parameters:
01024     None.
01025     
01026    Returns:
01027     Nothing.
01028 */
01029 void  CHTTPContext::ParseFirstRequestLine()
01030 {
01031     static const char GetMethod[] = "GET ";
01032     static const char HeadMethod[] = "HEAD ";
01033     static const char PostMethod[] = "POST ";
01034     const char*       pURIStart = NULL;
01035     const char*       pURISrc = NULL;
01036     char*             pURIDest = NULL;
01037     char              UnescapedChar = 0;
01038     int               CharsToEscape = 0;
01039 
01040     // Advance to next state.
01041     m_ParseState = STATE_HEADERS;
01042 
01043     // This is the first line which indicates method being requested
01044     // (ie. GET, HEAD, POST).
01045     if (0 == strncmp(m_LineBuffer, GetMethod, sizeof(GetMethod)-1))
01046     {
01047         // Assume a HTTP/1.0 GET request for now.  Will know later if we
01048         // need to downgrade it to a 0.9 request when version isn't sent.
01049         m_RequestType = TYPE_GET_1_0;
01050         pURISrc = &m_LineBuffer[sizeof(GetMethod)-1];
01051     }
01052     else if (0 == strncmp(m_LineBuffer, HeadMethod, sizeof(HeadMethod)-1))
01053     {
01054         m_RequestType = TYPE_HEAD;
01055         pURISrc = &m_LineBuffer[sizeof(HeadMethod)-1];
01056     }
01057     else if (0 == strncmp(m_LineBuffer, PostMethod, sizeof(PostMethod)-1))
01058     {
01059         m_RequestType = TYPE_POST;
01060         pURISrc = &m_LineBuffer[sizeof(PostMethod)-1];
01061     }
01062     else
01063     {
01064         // Don't recognize this method so mark it as bad.
01065         m_RequestType = TYPE_BAD_REQUEST;
01066         
01067         // See if the application recognizes and wants to handle it by sending
01068         // it the full request line.
01069         StartBadRequest(m_LineBuffer);
01070         return;
01071     }
01072 
01073     // Skip additional whitespace that might precede URI even though there
01074     // shouldn't be any just to be lenient.
01075     while (' ' == *pURISrc)
01076     {
01077         pURISrc++;
01078     }
01079     pURIStart = pURISrc;
01080     pURIDest = (char*)(void*)pURISrc;
01081     
01082     // Save away the URI character by character until whitespace or end
01083     // of line is encountered.
01084     while(' '  != *pURISrc &&
01085           '\0' != *pURISrc)
01086     {
01087         char CurrChar = *pURISrc;
01088         
01089         // Unescape % HEX HEX portions of the URI
01090         if ('%' == CurrChar)
01091         {
01092             CharsToEscape = 2;
01093             UnescapedChar = 0;
01094         }
01095         else if (CharsToEscape)
01096         {
01097             UnescapedChar <<= 4;
01098             
01099             if (CurrChar >= 'a' && CurrChar <= 'f')
01100             {
01101                 CurrChar = (CurrChar - 'a') + 10;
01102             }
01103             else if (CurrChar >= 'A' && CurrChar <= 'F')
01104             {
01105                 CurrChar = (CurrChar - 'A') + 10;
01106             }
01107             else if (CurrChar >= '0' && CurrChar <= '9')
01108             {
01109                 CurrChar = CurrChar - '0';
01110             }
01111             else
01112             {
01113                 // Not a hex digit.
01114                 CurrChar = 0;
01115             }
01116             
01117             UnescapedChar |= (CurrChar & 0xF);
01118             
01119             if (0 == --CharsToEscape)
01120             {
01121                 *pURIDest++ = UnescapedChar;
01122             }
01123         }
01124         else
01125         {
01126             *pURIDest++ = CurrChar;
01127         }
01128         pURISrc++;
01129     }
01130     
01131     if (' ' != *pURISrc)
01132     {
01133         // Protocol string didn't follow URI so must be a HTTP/0.9 request.
01134         if (TYPE_GET_1_0 == m_RequestType)
01135         {
01136             m_RequestType = TYPE_GET_0_9;
01137             // This will have been the last request line for a v0.9 request so
01138             // advance to the finished state.
01139             m_ParseState = STATE_DONE;
01140         }
01141         else
01142         {
01143             m_RequestType = TYPE_BAD_REQUEST;
01144 
01145             // See if the application recognizes and wants to handle it by sending
01146             // it the full request line.
01147             StartBadRequest(m_LineBuffer);
01148             return;
01149         }
01150     }
01151     *pURIDest = '\0';
01152     
01153     StartRequestHandler(pURIStart);
01154 }
01155 
01156 
01157 /* Attempts to parse the header request lines until a blank request line is
01158    encountered which indicates that start of the content.
01159 
01160    Parameters:
01161     None.
01162     
01163    Returns:
01164     Nothing.
01165 */
01166 void  CHTTPContext::ParseHeaderRequestLine()
01167 {
01168     static const char ContentLengthName[] = "Content-Length";
01169     char*             pName = m_LineBuffer;
01170     char*             pValue = NULL;
01171     char*             pCurr = m_LineBuffer;
01172     
01173     if ('\0' == m_LineBuffer[0])
01174     {
01175         // Let the request handler know that we have reached the last header.
01176         if (m_SendRequestHeadersToHandler)
01177         {
01178             m_pRequestHandlerContext->EndRequestHeaders();
01179         }
01180         
01181         if (m_RequestContentLength)
01182         {
01183             // Determine if the request handler is interested in any request
01184             // content data that might follow the headers.
01185             m_ParseState = STATE_CONTENT;
01186             m_SendRequestContentToHandler = m_pRequestHandlerContext->BeginRequestContent(m_RequestContentLength);
01187         }
01188         else
01189         {
01190             // There is no request content from the client so server can start
01191             // to send its response now.
01192             m_ParseState = STATE_DONE;
01193         }
01194         
01195         return;
01196     }
01197 
01198     // Find the colon character which terminates the field name.  A whitespace
01199     // character at the beginning of a header line indicates a line
01200     // continuation so handle that as well.
01201     while (':' != *pCurr &&
01202            ' ' != *pCurr &&
01203            '\t' != *pCurr )
01204     {
01205         pCurr++;
01206     }
01207     *pCurr++ = '\0';
01208     
01209     // Skip over whitespace to find start of value.
01210     while (' ' == *pCurr ||
01211            '\t' == *pCurr)
01212     {
01213         pCurr++;
01214     }
01215     pValue = pCurr;
01216     
01217     // Check for the Content-Length field which tells the server if it should
01218     // expect the request to have a content body.
01219     if (0 == strcasecmp(pName, ContentLengthName))
01220     {
01221         m_RequestContentLength = strtoul(pValue, NULL, 10);
01222     }
01223     
01224     // Send the header to request handler context interface if it has requested
01225     // the server to do so.
01226     if (m_SendRequestHeadersToHandler)
01227     {
01228         m_pRequestHandlerContext->WriteRequestHeader(pName, pValue);
01229     }
01230 }
01231 
01232 
01233 /* Kicks off the process of handling client specified request.
01234    
01235    Parameters:
01236     pURI is the name of the URI being requested.
01237         
01238    Returns:
01239     Nothing.
01240 */
01241 void CHTTPContext::StartRequestHandler(const char* pURI)
01242 {
01243     // Start request by first letting application provider a handler and if it
01244     // doesn't use this object's default request handler interface instead.
01245     switch (m_RequestType)
01246     {
01247     case TYPE_GET_0_9:
01248     case TYPE_GET_1_0:
01249         StartGetRequest(pURI);
01250         break;
01251     case TYPE_HEAD:
01252         StartHeadRequest(pURI);
01253         break;
01254     case TYPE_POST:
01255         StartPostRequest(pURI);
01256         break;
01257     default:
01258         assert ( FALSE );
01259         break;
01260     }
01261     
01262     // Should have a request handler now: either from application or this
01263     // context object.
01264     assert ( m_pRequestHandlerContext );
01265     
01266     // Ask the request handler interface if it wants each request header sent
01267     // to it.  There will be no header for HTTP/0.9 GET request which skips the
01268     // header state.
01269     if (STATE_HEADERS == m_ParseState)
01270     {
01271         m_SendRequestHeadersToHandler = m_pRequestHandlerContext->BeginRequestHeaders();
01272     }
01273 }
01274 
01275 
01276 /* Handles GET requests by first allowing the application to handler it via a
01277    register request handler.  If it doesn't want to handle the request then the
01278    server will attempt to satisfy the request from the file system that was
01279    registers at bind time.
01280    
01281    Parameters:
01282     pURI is the name of the URI being requested.
01283         
01284    Returns:
01285     Nothing.
01286 */
01287 void CHTTPContext::StartGetRequest(const char* pURI)
01288 {
01289     // Load the requested file if possible or the file not found
01290     // content if requested file was not found.
01291     TRACE("HTTP: GET '%s' from: ", pURI);
01292     NetworkPrintRemoteAddress(m_pPCB);
01293     TRACE("\r\n");
01294     
01295     if (m_pHTTPServer->m_pRequestHandler)
01296     {
01297         // First let the application attempt to handle the GET request.
01298         m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandleGetRequest(pURI);
01299     }
01300     if (!m_pRequestHandlerContext)
01301     {
01302         // The application doesn't want to handle the GET request so use the
01303         // default server behaviour.
01304         OpenFile(pURI);
01305         m_pRequestHandlerContext = this;
01306     }
01307 }
01308 
01309 
01310 /* Handles HEAD requests by first allowing the application to handler it via a
01311    register request handler.  If it doesn't want to handle the request then the
01312    server will attempt to satisfy the request from the file system that was
01313    registers at bind time.
01314    
01315    Parameters:
01316     pURI is the name of the URI being requested.
01317         
01318    Returns:
01319     Nothing.
01320 */
01321 void CHTTPContext::StartHeadRequest(const char* pURI)
01322 {
01323     // Load the requested file if possible or the file not found
01324     // content if requested file was not found.
01325     TRACE("HTTP: HEAD '%s' from: ", pURI);
01326     NetworkPrintRemoteAddress(m_pPCB);
01327     TRACE("\r\n");
01328     
01329     if (m_pHTTPServer->m_pRequestHandler)
01330     {
01331         // First let the application attempt to handle the HEAD request.
01332         m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandleHeadRequest(pURI);
01333     }
01334 
01335     if (!m_pRequestHandlerContext)
01336     {
01337         // The application doesn't want to handle the HEAD request so use the
01338         // default server behaviour.
01339         OpenFile(pURI);
01340         m_pRequestHandlerContext = this;
01341     }
01342 }
01343 
01344 
01345 /* Handles POST requests by allowing the application to handler it via a
01346    registered request handler.  If it doesn't want to handle the request then
01347    the server will treat it as an unimplemented request.
01348    
01349    Parameters:
01350     pURI is the name of the URI being requested.
01351         
01352    Returns:
01353     Nothing.
01354 */
01355 void CHTTPContext::StartPostRequest(const char* pURI)
01356 {
01357     // Load the requested file if possible or the file not found
01358     // content if requested file was not found.
01359     TRACE("HTTP: POST '%s' from: ", pURI);
01360     NetworkPrintRemoteAddress(m_pPCB);
01361     TRACE("\r\n");
01362     
01363     if (m_pHTTPServer->m_pRequestHandler)
01364     {
01365         // First let the application attempt to handle the POST request.
01366         m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandlePostRequest(pURI);
01367     }
01368 
01369     if (!m_pRequestHandlerContext)
01370     {
01371         // The application doesn't want to handle the POST request.
01372         m_pRequestHandlerContext = this;
01373     }
01374 }
01375 
01376 
01377 /* Handles unknown/bad HTTP client requests by sending back an appropriate
01378    error response.
01379    
01380    Parameters:
01381     pRequest is the complete request line which wasn't recognized by the server.
01382         
01383    Returns:
01384     Nothing.
01385 */
01386 void CHTTPContext::StartBadRequest(const char* pRequest)
01387 {
01388     if (m_pHTTPServer->m_pRequestHandler)
01389     {
01390         // First let the application attempt to handle this request instead.
01391         m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandleBadRequest(pRequest);
01392     }
01393     if (!m_pRequestHandlerContext)
01394     {
01395         // The application doesn't want to handle the bad request so just
01396         // return an appropriate bad request response.
01397         m_pRequestHandlerContext = this;
01398     }
01399 
01400     // Ask the request handler interface if it wants each request header sent
01401     // to it.
01402     m_SendRequestHeadersToHandler = m_pRequestHandlerContext->BeginRequestHeaders();
01403 }
01404 
01405 
01406 /* Called from HTTPRecv() to open the specified file that has been requested by
01407    the HTTP client.  In addition to opening the specified file, it will also
01408    fill in the headers to match the content being sent vack.  It also handles
01409    sending back the correct header if the requested file isn't found.
01410    
01411    Parameters:
01412     pURI is the name of the URI being requested.
01413         
01414    Returns:
01415     Nothing.
01416 */
01417 void CHTTPContext::OpenFile(const char* pURI)
01418 {
01419     static const char NotFoundHTML[] = "<html>"
01420                                        "<body><h2>404: File not found.</h2></body>"
01421                                        "</html>\r\n";
01422     // The content types supported by this HTTP server.
01423     static const char  ContentHTML[] = "Content-type: text/html\r\n";
01424     static const char  ContentGIF[] = "Content-type: image/gif\r\n";
01425     static const char  ContentPNG[] = "Content-type: image/png\r\n";
01426     static const char  ContentJPG[] = "Content-type: image/jpeg\r\n";
01427     static const char  ContentBMP[] = "Content-type: image/bmp\r\n";
01428     static const char  ContentICO[] = "Content-type: image/x-icon\r\n";
01429     static const char  ContentStream[] = "Content-type: application/octet-stream\r\n";
01430     static const char  ContentJScript[] = "Content-type: application/x-javascript\r\n";
01431     static const char  ContentCSS[] = "Content-type: text/css\r\n";
01432     static const char  ContentFlash[] = "Content-type: application/x-shockwave-flash\r\n";
01433     static const char  ContentXML[] = "Content-type: text/xml\r\n";
01434     static const char  ContentDefault[] = "Content-type: text/plain\r\n";
01435 
01436     // Table used to map file extensions to above content type headers.
01437     #define CONTENT_HEADER(X) X, sizeof(X)-1
01438     static const struct
01439     {
01440         // File extension.
01441         const char*     pExtension;
01442         // Content header to be used for this file extension type.
01443         const char*     pContentHeader;
01444         // Length of pContentHeader string without NULL terminator.
01445         unsigned int    ContentHeaderLength;
01446     } ContentMapping[] =
01447     {
01448         {".html",  CONTENT_HEADER(ContentHTML)},
01449         {".htm",   CONTENT_HEADER(ContentHTML)},
01450         {".gif",   CONTENT_HEADER(ContentGIF)},
01451         {".png",   CONTENT_HEADER(ContentPNG)},
01452         {".jpg",   CONTENT_HEADER(ContentJPG)},
01453         {".bmp",   CONTENT_HEADER(ContentBMP)},
01454         {".ico",   CONTENT_HEADER(ContentICO)},
01455         {".class", CONTENT_HEADER(ContentStream)},
01456         {".cls",   CONTENT_HEADER(ContentStream)},
01457         {".js",    CONTENT_HEADER(ContentJScript)},
01458         {".css",   CONTENT_HEADER(ContentCSS)},
01459         {".swf",   CONTENT_HEADER(ContentFlash)},
01460         {".xml",   CONTENT_HEADER(ContentXML)}
01461     };
01462     // Local variables.
01463     size_t  i;
01464     char    LocalFilename[HTTP_MAX_FILENAME+1];
01465     
01466     // Attempt to open the file.
01467     if (0 == strcmp(pURI, "/"))
01468     {
01469         // Iterate through the list of default root filenames.
01470         for (i = 0 ; !m_pFile && i < ARRAYSIZE(g_DefaultFilenames) ; i++)
01471         {
01472             snprintf(LocalFilename, ARRAYSIZE(LocalFilename), "/%s%s", m_pHTTPServer->m_pRootPathname, g_DefaultFilenames[i]);
01473             m_pFile = fopen(LocalFilename, "r");
01474         }
01475     }
01476     else
01477     {
01478         // Attempt to open the specified URI.
01479         snprintf(LocalFilename, ARRAYSIZE(LocalFilename), "/%s%s", m_pHTTPServer->m_pRootPathname, pURI);
01480         m_pFile = fopen(LocalFilename, "r");
01481     }
01482     
01483     // Setup the content type field based on whether the file was found or not.
01484     if (!m_pFile)
01485     {
01486         // The file open failed so return a 404 error with HTML.
01487         m_pContentType = ContentHTML;
01488         m_ContentTypeLength = sizeof(ContentHTML) - 1;
01489         m_pErrorHTML = NotFoundHTML;
01490         m_ErrorHTMLLength = sizeof(NotFoundHTML) - 1;
01491     }
01492     else
01493     {
01494         const char* pExtension;
01495         
01496         // Configure the content type header based on filename extension.
01497         pExtension = strrchr(LocalFilename, '.');
01498         for (i = 0 ; pExtension && i < ARRAYSIZE(ContentMapping) ; i++)
01499         {
01500             if (0 == strcmp(pExtension, ContentMapping[i].pExtension))
01501             {
01502                 m_pContentType = ContentMapping[i].pContentHeader;
01503                 m_ContentTypeLength = ContentMapping[i].ContentHeaderLength;
01504                 break;
01505             }
01506         }
01507         
01508         // If the type couldn't be found default to plain.
01509         if (!m_pContentType)
01510         {
01511             m_pContentType = ContentDefault;
01512             m_ContentTypeLength = sizeof(ContentDefault) - 1;
01513         }
01514     }    
01515 }
01516 
01517 
01518 /* Prepares the headers and content to be sent back to the HTTP client as a
01519    response to its latest request.  It then starts sending out the first set of
01520    response packets.
01521    
01522    Parameters:
01523     None.
01524         
01525    Returns:
01526     Nothing.
01527 */
01528 void CHTTPContext::PrepareResponse()
01529 {
01530     assert ( STATE_DONE == m_ParseState );
01531 
01532     PrepareResponseHeaders();
01533     
01534     if (!m_pRequestHandlerContext->BeginResponseContent())
01535     {
01536         // The application has no content to send so release it.
01537         m_pRequestHandlerContext->Release();
01538         m_pRequestHandlerContext = NULL;
01539     }
01540 }
01541 
01542 
01543 /* Prepares the headers and content to be sent back to the HTTP client as a
01544    response to its latest request.  It then starts sending out the first set of
01545    response packets.
01546    
01547    Parameters:
01548     None.
01549         
01550    Returns:
01551     Nothing.
01552 */
01553 void CHTTPContext::PrepareResponseHeaders()
01554 {
01555     static const char   BlankLine[] = "\r\n";
01556     const char*         pStatusLine = NULL;
01557     const char*         pExtraHeaders = NULL;
01558     size_t              StatusLineLength = 0;
01559     size_t              ExtraHeadersLength = 0;
01560     size_t              i = 0;
01561     
01562     if (TYPE_GET_0_9 == m_RequestType)
01563     {
01564         // Don't return any headers for a HTTP/0.9 GET request.
01565         m_HeaderArray[0].pHeaderString = NULL;
01566         m_HeaderArray[0].HeaderLength = 0;
01567         return;
01568     }
01569     
01570     // Prepare the response status.
01571     pStatusLine = m_pRequestHandlerContext->GetStatusLine(&StatusLineLength);
01572     
01573     // Application must return a status line.
01574     assert ( pStatusLine && StatusLineLength > 0 );
01575     
01576     m_HeaderArray[0].pHeaderString = pStatusLine;
01577     m_HeaderArray[0].HeaderLength = StatusLineLength;
01578     
01579     // The second header returned will always be the server type, no
01580     // matter what response we send back.
01581     m_HeaderArray[1].pHeaderString = m_pHTTPServer->m_ServerHeader;
01582     m_HeaderArray[1].HeaderLength = m_pHTTPServer->m_ServerHeaderLength;
01583 
01584     // Find out what other headers the application would like to send back to
01585     // the HTTP client.
01586     pExtraHeaders = m_pRequestHandlerContext->GetResponseHeaders(&ExtraHeadersLength);
01587     if (pExtraHeaders)
01588     {
01589         m_HeaderArray[2].pHeaderString = pExtraHeaders;
01590         m_HeaderArray[2].HeaderLength = ExtraHeadersLength;
01591         i = 3;
01592     }
01593     else
01594     {
01595         // Won't be using all of the elements of m_HeaderArray.
01596         i = 2;
01597     }
01598     
01599     // Finally send the blank line header which separates the headers from the
01600     // entity body.
01601     m_HeaderArray[i].pHeaderString = BlankLine;
01602     m_HeaderArray[i].HeaderLength = sizeof(BlankLine) -1;
01603 }
01604 
01605 /* Attempts to allocate a SBuffer object for reading the data into.  If such a
01606    buffer is available then read HTTP headers and file data into the buffer.
01607    If the buffer can't be allocated, then add this client to a queue to be
01608    processed later.
01609 
01610    Parameters:
01611     None.
01612         
01613    Returns:
01614     Nothing.
01615 */
01616 void CHTTPContext::ReadContent()
01617 {
01618     // Local variables.
01619     SBuffer*     pWriteBuffer = m_BufferQueue[m_BufferQueueHead];
01620     unsigned int BytesLeftInBuffer;
01621     char*        pData;
01622     unsigned int i;
01623     
01624     
01625     // Return now if there is already a buffer filled with data in the process
01626     // of being written out to the connection.  Also return if there is no more
01627     // data to be sent to this client.
01628     if (pWriteBuffer ||
01629         (m_HeaderIndex >= ARRAYSIZE(m_HeaderArray) &&
01630          !m_pRequestHandlerContext))
01631     {
01632         return;
01633     }
01634 
01635     // Attempt to allocate a new SBuffer.
01636     pWriteBuffer = m_pHTTPServer->AllocateBuffer(this);
01637     if (!pWriteBuffer)
01638     {
01639         // There is no buffer available at this time.  Will be called back
01640         // later when one becomes available.
01641         return;
01642     }
01643     assert ( 0 == pWriteBuffer->BytesToWrite );
01644     assert ( 0 == pWriteBuffer->UnacknowledgedBytes );
01645     
01646     // Buffer is free.  Now fill it with headers and file data.
01647     BytesLeftInBuffer = sizeof(pWriteBuffer->Data);
01648     pData = pWriteBuffer->Data;
01649     pWriteBuffer->pWrite = pData;
01650     
01651     // Copy remaining headers into free buffer.
01652     for (i = m_HeaderIndex ; 
01653          i < ARRAYSIZE(m_HeaderArray) ; 
01654          i++)
01655     {
01656         unsigned int BytesToCopy;
01657         unsigned int HeaderBytesLeft;
01658         
01659         assert ( i == m_HeaderIndex );
01660         
01661         if (!m_HeaderArray[i].pHeaderString)
01662         {
01663             // Not all (or any) of the headers were used so mark as done and
01664             // no need to copy any more so break out of loop.
01665             m_HeaderIndex = ARRAYSIZE(m_HeaderArray);
01666             break;
01667         }
01668         
01669         // Determine how many bytes are to be copied.
01670         HeaderBytesLeft = m_HeaderArray[i].HeaderLength - 
01671                           m_HeaderOffset;
01672         if (HeaderBytesLeft > BytesLeftInBuffer)
01673         {
01674             BytesToCopy = BytesLeftInBuffer;
01675         }
01676         else
01677         {
01678             BytesToCopy = HeaderBytesLeft;
01679         }
01680         
01681         // Perform the copy.
01682         memcpy(pData, 
01683                m_HeaderArray[i].pHeaderString + m_HeaderOffset,
01684                BytesToCopy);
01685                
01686         // Update buffer information.
01687         pData += BytesToCopy;
01688         BytesLeftInBuffer -= BytesToCopy;
01689         HeaderBytesLeft -= BytesToCopy;
01690         if (0 == HeaderBytesLeft)
01691         {
01692             // Advance to next header.
01693             m_HeaderIndex++;
01694             m_HeaderOffset = 0;
01695         }
01696         else
01697         {
01698             // Advance offset within this header and exit since buffer is full.
01699             assert ( 0 == BytesLeftInBuffer );
01700             
01701             m_HeaderOffset += BytesToCopy;
01702             break;
01703         }
01704     }
01705     
01706     // Copy file data into buffer for sending.
01707     if (m_pRequestHandlerContext)
01708     {
01709         size_t BytesRead;
01710         
01711         BytesRead = m_pRequestHandlerContext->ReadResponseContentBlock(pData, BytesLeftInBuffer);
01712         if (BytesRead < BytesLeftInBuffer)
01713         {
01714             // Must have hit end of file, so close it.
01715             m_pRequestHandlerContext->EndResponseContent();
01716             m_pRequestHandlerContext->Release();
01717             m_pRequestHandlerContext = NULL;
01718         }
01719         pData += BytesRead;
01720         BytesLeftInBuffer -= BytesRead;
01721     }
01722     
01723     // Update the buffer valid byte count.
01724     pWriteBuffer->BytesToWrite = ARRAYSIZE(pWriteBuffer->Data) - 
01725                                  BytesLeftInBuffer;
01726                                  
01727     // Update the client context to indicate that it now has a buffer ready to
01728     // be written out to the client connection.
01729     m_BufferQueue[m_BufferQueueHead] = pWriteBuffer;
01730 }
01731 
01732 
01733 /* Attempts to send HTTP client data already read into buffer and also reads
01734    in more data to the buffer for sending as necessary.  This data will be
01735    composed of HTTP headers and file data.
01736    
01737    Parameters:
01738     None.
01739     
01740    Returns:
01741     Nothing.
01742 */
01743 void CHTTPContext::SendResponse()
01744 {
01745     int          ConnectionClosed;
01746 
01747     // Attempt to send any data still in write buffer.
01748     ConnectionClosed = WriteData();
01749     
01750     // Return now if the connection was closed.
01751     if (ConnectionClosed)
01752     {
01753         return;
01754     }
01755     
01756     // Read in more content if possible.
01757     ReadContent();
01758 
01759     // UNDONE: Once I have a performance test available, try removing this to see if it has any impact on performance.
01760     //         Also want to wait for DMA based driver.
01761     // Attempt to send data that was just read into buffer.
01762     WriteData();
01763 }
01764 
01765 
01766 /* Sends any remaining response data to the HTTP client and closes the
01767    connection once the data has been sent.
01768    
01769    Parameters:
01770     None.
01771 
01772    Returns:
01773     1 if the last chunk was sent and the connection closed and 0 otherwise.
01774 */
01775 int CHTTPContext::WriteData()
01776 {
01777     // Local variables.
01778     err_t    WriteResult = ERR_MEM;
01779     size_t   BytesToWrite;
01780     size_t   SendBufferSize;
01781     SBuffer* pWriteBuffer = m_BufferQueue[m_BufferQueueHead];
01782 
01783     // Just return if there isn't a write buffer with data to send.
01784     if (!pWriteBuffer)
01785     {
01786         return 0;
01787     }
01788 
01789     // Don't try to send more bytes than what the connection will accept.
01790     BytesToWrite = pWriteBuffer->BytesToWrite;
01791     SendBufferSize = tcp_sndbuf(m_pPCB);
01792     if (BytesToWrite > SendBufferSize)
01793     {
01794         BytesToWrite = SendBufferSize;
01795     }
01796     
01797     // Keep attempting to send response data and shrinking the write until it
01798     // succeeds or it shrinks down to 0 bytes.
01799     while (BytesToWrite > 0)
01800     {
01801         // Attempt to write the response data out to the PCB.
01802         WriteResult = tcp_write(m_pPCB, 
01803                                 pWriteBuffer->pWrite, 
01804                                 BytesToWrite,
01805                                 0);
01806         if (ERR_OK == WriteResult)
01807         {
01808             // Write was successful so update buffer descriptors in the context
01809             // objects.
01810             pWriteBuffer->pWrite += BytesToWrite;
01811             pWriteBuffer->BytesToWrite -= BytesToWrite;
01812             pWriteBuffer->UnacknowledgedBytes += BytesToWrite;
01813             
01814             // Successfully queued up data for sending so reset retry count.
01815             m_RetryCount = 0;
01816             break;
01817         }
01818         else if (ERR_MEM == WriteResult)
01819         {
01820             // Failed to perform the write due to out of memory error.
01821             // Check to see if the send queue if full.  If not, half the write
01822             // size and try again.
01823             if (0 == tcp_sndqueuelen(m_pPCB))
01824             {
01825                 break;
01826             }
01827             // UNDONE: Rather than half it, subtract the size of a MSS.
01828             BytesToWrite >>= 1;
01829         }
01830         else
01831         {
01832             // Received unexpected error.
01833             break;
01834         }
01835     }
01836     
01837     if (0 == pWriteBuffer->BytesToWrite)
01838     {
01839         // There are no more bytes to write from the buffer so advance the
01840         // header pointer to the next item in the buffer queue.
01841         m_BufferQueueHead = (m_BufferQueueHead + 1) & 1;
01842 
01843         // If there are no more bytes to send and the client has finished sending
01844         // its request then we should attempt to close the connection.
01845         if (m_HeaderIndex >= ARRAYSIZE(m_HeaderArray) &&
01846             !m_pRequestHandlerContext &&
01847             STATE_DONE == m_ParseState)
01848         {
01849             TRACE("HTTP: Response send completed to: ");
01850             NetworkPrintRemoteAddress(m_pPCB);
01851             TRACE("\r\n");
01852             CloseConnection();
01853         
01854             return 1;
01855         }
01856     }
01857     
01858     return 0;
01859 }
01860 
01861 
01862 /* Called for a context which was waiting for a buffer when a buffer is freed
01863    by another connection.
01864    
01865    Parameters:
01866     None.
01867     
01868    Returns:
01869     Nothing.
01870 */
01871 void CHTTPContext::BufferAvailable()
01872 {
01873     // Allow the context to allocate the buffer and fill it in with data.
01874     ReadContent();
01875     
01876     // Allow the context to send the data that was just read into the
01877     // buffer.
01878     SendResponse();
01879 
01880     // Tell lwIP to send the segments for this PCB as it normally only
01881     // queues up segments automatically for the PCB whose callback is
01882     // currently being called.
01883     tcp_output(m_pPCB);
01884 }
01885 
01886 
01887 
01888 /* Constructor */
01889 CHTTPServer::CHTTPServer()
01890 {
01891     m_pHTTPListenPCB = NULL;
01892     m_pContextWaitingHead = NULL;
01893     m_pContextWaitingTail = NULL;
01894     m_pRootPathname = NULL;
01895     m_pRequestHandler = NULL;
01896     m_ServerHeaderLength = 0;;
01897     m_ServerHeader[0] = '\0';
01898     memset(m_BufferPool, 0, sizeof(m_BufferPool));
01899 }
01900 
01901 
01902 /* Attach IRequestHandler to the HTTP server to allow application to add
01903    custom GET/POST handling.
01904 
01905    Parameters:
01906     pHandler is a pointer to the application specific request handler object
01907         to be used by the HTTP server when it receives GET/POST requests.
01908 
01909    Returns:
01910     Returns 0 on successful attachment and a positive value otherwise.
01911 */
01912 int CHTTPServer::AttachRequestHandler(IHTTPRequestHandler* pHandler)
01913 {
01914     m_pRequestHandler = pHandler;
01915     
01916     return 0;
01917 }
01918 
01919 
01920 /* Initializes the HTTP server using the raw lwIP APIs.  This consists of
01921    creating a PCB (low level lwIP socket), binding it to port 80 and then
01922    listening for connections on this port.  When subsequent connection comes
01923    into the HTTP server, a callback will be made into the HTTPAccept()
01924    function that is registered here as well.
01925    
01926    Parameters:
01927     pRootPathame is the pathname of the default root directory from which the
01928         HTTP GET requests are satisfied.
01929     pServerName is the name of the server to be returned to the HTTP client
01930         in the Server header.
01931     BindPort is the TCP/IP port to which the HTTP server should listen for
01932         incoming requests.  The default port for HTTP would be 80.
01933     
01934    Returns:
01935     0 on success and a positive error code otherwise.
01936 */
01937 int CHTTPServer::Bind(const char*    pRootPathname,
01938                       const char*    pServerName,
01939                       unsigned short BindPort)
01940 {
01941     int         Return = 1;
01942     tcp_pcb*    pcb = NULL;
01943     int         Length = -1;
01944     err_t       Result;
01945 
01946     // Validate parameters.
01947     assert ( pRootPathname && pServerName );
01948     
01949     // Create server header string.
01950     Length = snprintf(m_ServerHeader, ARRAYSIZE(m_ServerHeader), "Server: %s\r\n", pServerName);
01951     if (Length < 0 || Length >= (int)ARRAYSIZE(m_ServerHeader))
01952     {
01953         printf("error: '%s' is too long for the HTTP server name string.\r\n", pServerName);
01954         goto Error;
01955     }
01956     m_ServerHeaderLength = (size_t)Length;
01957     
01958     // Save away root directory name.
01959     m_pRootPathname = pRootPathname;
01960     
01961     // Create the PCB (low level lwIP socket) for the HTTP server to listen to
01962     // on well known HTTP port 80.
01963     pcb = tcp_new();
01964     if (!pcb)
01965     {
01966         printf("error: Failed to create new connection for HTTP server to listen upon.\r\n");
01967         goto Error;
01968     }
01969     
01970     // Bind the PCB to port 80.
01971     Result = tcp_bind(pcb, IP_ADDR_ANY, BindPort);
01972     if (ERR_OK != Result)
01973     {
01974         printf("error: HTTP server failed to bind to port %d.\r\n", BindPort);
01975         goto Error;
01976     }
01977     
01978     // Listen on this PCB.
01979     m_pHTTPListenPCB = tcp_listen(pcb);
01980     if (!m_pHTTPListenPCB)
01981     {
01982         printf("error: HTTP server failed to listen on port %d.\r\n", BindPort);
01983         goto Error;
01984     }
01985     
01986     // pcb was freed by successful tcp_listen() call.
01987     pcb = NULL;
01988     
01989     // When connections come in on port 80, we want lwIP to call our
01990     // HTTPAccept() function to handle the connection.
01991     tcp_arg(m_pHTTPListenPCB, this);
01992     tcp_accept(m_pHTTPListenPCB, HTTPAccept);
01993     
01994     // Return to the main program since there is nothing left to do until a
01995     // client tries to make a connection on port 80, at which time lwIP will
01996     // get the connection packet from the Ethernet driver and make the callback
01997     // into HTTPAccept().
01998     
01999     Return = 0;
02000 Error:
02001     if (pcb)
02002     {
02003         tcp_close(pcb);
02004     }
02005     return Return;
02006 }
02007 
02008 
02009 /* Called by lwIP whenever a connection is made on the PCB which is listening
02010    on port 80 for HTTP requests.  The main task of this callback is to create
02011    any necessary state to track this unique HTTP client connection and then
02012    setup the callbacks to be called when activity occurs on this client
02013    connection. 
02014    
02015    Parameters:
02016     pvArg is the value set on the listening PCB through the call to tcp_arg in
02017         HTTPInit() which is a pointer to the intitialized network object.
02018     pNewPCB is a pointer to the new PCB that was just allocated by the lwIP
02019         stack for this new client connection on port 80.
02020     err is passed in from the lwIP stack to indicate if an error occurred.
02021     
02022    Returns:
02023     ERR_MEM if we don't have enough memory for this connection and ERR_OK 
02024     otherwise.
02025 */
02026 err_t CHTTPServer::HTTPAccept(void* pvArg, tcp_pcb* pNewPCB, err_t err)
02027 {
02028     CHTTPServer*    pHTTPServer = (CHTTPServer*)pvArg;
02029     CHTTPContext*   pHTTPContext = NULL;
02030     (void)err;
02031     
02032     // Validate parameters.
02033     assert ( pvArg );
02034     assert ( pNewPCB );
02035     
02036     // Let user know that we have just accepted a new connection.
02037     TRACE("HTTP: Accepting client connection from: ");
02038     NetworkPrintRemoteAddress(pNewPCB);
02039     TRACE("\r\n");
02040     
02041     // Let the lwIP stack know that the HTTP application has successfuly
02042     // received the client connection on the listening PCB.
02043     tcp_accepted(pHTTPServer->m_pHTTPListenPCB);
02044 
02045     // UNDONE: Remove this new call and use a pool instead.
02046     // Allocate memory to track context for this client connection and
02047     // associate with the PCB.
02048     pHTTPContext = new CHTTPContext(pHTTPServer, pNewPCB);
02049     if (!pHTTPContext)
02050     {
02051         TRACE("HTTP: Failed to allocate client context for: ");
02052         NetworkPrintRemoteAddress(pNewPCB);
02053         TRACE("\r\n");
02054         
02055         // lwIP will shutdown this connection when it get back a return value
02056         // other than ERR_OK.
02057         return ERR_MEM;
02058     }
02059     
02060     return ERR_OK;
02061 }
02062 
02063 
02064 /* Removes the specified context from the linked list of waiting contexts.
02065    
02066    Parameters:
02067     pHTTPContext is a pointer to the context object for the client connection
02068         to be removed.
02069         
02070    Returns:
02071     Nothing.
02072 */
02073 void CHTTPServer::RemoveWaitingContext(CHTTPContext* pHTTPContext)
02074 {
02075     // Find this node in the waiting queue and remove it.
02076     CHTTPContext*  pPrev = NULL;
02077     CHTTPContext*  pCurr = m_pContextWaitingHead;
02078     while(pCurr)
02079     {
02080         CHTTPContext* pNext = pCurr->GetNext();
02081 
02082         if (pCurr == pHTTPContext)
02083         {
02084             // Found this context.  Now remove it and exit loop.
02085             pCurr->RemovedFromWaitingList(pPrev);
02086             if (!pPrev)
02087             {
02088                 // This node was at the head so update the head pointer.
02089                 m_pContextWaitingHead = pNext;
02090             }
02091             if (!pNext)
02092             {
02093                 // This node was at the tail so reset the tail to previous node.
02094                 m_pContextWaitingTail = pPrev;
02095             }
02096             break;
02097         }
02098         pPrev = pCurr;
02099         pCurr = pNext;
02100     }
02101     // We should always find this context in the list.
02102     assert ( pCurr == pHTTPContext );
02103 }
02104 
02105 
02106 /* Attempts to allocate a SBuffer object into which content can be placed for
02107    sending back to the HTTP client.  If it fails to find a free buffer in the
02108    server's pool then it will add the client context to a queue and return
02109    NULL.
02110    
02111    Parameters:
02112     pHTTPContext is a pointer to the context asking for a buffer object.
02113     
02114    Returns:
02115     A pointer to a free SBuffer object or NULL if none are free.
02116 */
02117 SBuffer* CHTTPServer::AllocateBuffer(CHTTPContext* pHTTPContext)
02118 {
02119     CHTTPContext* pPrev = NULL;
02120     
02121     // Validate parameters.
02122     assert ( pHTTPContext );
02123     
02124     // Local variables.
02125     size_t  i;
02126     
02127     // Just return NULL if this context is already waiting for a buffer.
02128     if (pHTTPContext->IsWaitingForBuffer())
02129     {
02130         return NULL;
02131     }
02132     
02133     // Search for a free buffer in the pool.
02134     for (i = 0 ; i < ARRAYSIZE(m_BufferPool) ; i++)
02135     {
02136         // A buffer is free if it has no bytes left to write to the connection
02137         // and the remote machine has acknowledged all previously written bytes
02138         // from this buffer.
02139         SBuffer* pBuffer = &(m_BufferPool[i]);
02140         
02141         if (0 == pBuffer->BytesToWrite &&
02142             0 == pBuffer->UnacknowledgedBytes)
02143         {
02144             return pBuffer;
02145         }
02146     }
02147     
02148     // Get here if there wasn't a free buffer in the pool so add this context
02149     // to the queue to be serviced later.
02150     assert (!pHTTPContext->GetNext());
02151     if (m_pContextWaitingTail)
02152     {
02153         // Add to end of list.
02154         pPrev = m_pContextWaitingTail;
02155         m_pContextWaitingTail = pHTTPContext;
02156     }
02157     else
02158     {
02159         // Adding entry to empty queue.
02160         assert (!m_pContextWaitingHead);
02161         
02162         m_pContextWaitingHead = pHTTPContext;
02163         m_pContextWaitingTail = pHTTPContext;
02164     }
02165     pHTTPContext->AddToWaitingList(pPrev);
02166     
02167     // Let calling context know that it will have to wait until later for a
02168     // buffer.
02169     return NULL;
02170 }
02171 
02172 
02173 /* Frees a SBuffer object back into the server's pool.  If there is a client
02174    context waiting in the queue, then callback into the context and let
02175    it use the buffer now that it is free.
02176    
02177    Parameters:
02178     pBuffer is a pointer to the buffer to be freed.
02179     
02180    Returns:
02181     Nothing.
02182 */
02183 void CHTTPServer::FreeBuffer(SBuffer* pBuffer)
02184 {
02185     // Validate parameters.
02186     assert ( pBuffer );
02187     
02188     // The SBuffer shouldn't be in use when this routine is called.
02189     assert ( 0 == pBuffer->BytesToWrite );
02190     assert ( 0 == pBuffer->UnacknowledgedBytes );
02191     
02192     // See if there are other client connection waiting for a buffer to become
02193     // free.  If so, give it an apportunity to make use of this one.
02194     if (m_pContextWaitingHead)
02195     {
02196         CHTTPContext* pHTTPContext = m_pContextWaitingHead;
02197         
02198         // Remove the context from the waiting list.
02199         m_pContextWaitingHead = pHTTPContext->GetNext();
02200         if (!m_pContextWaitingHead)
02201         {
02202             // We just emptied the list so clear tail pointer as well.
02203             assert ( m_pContextWaitingTail == pHTTPContext );
02204 
02205             m_pContextWaitingTail = NULL;
02206         }
02207         
02208         // Let context know that it is no longer in a list.
02209         pHTTPContext->RemovedFromWaitingList(NULL);
02210         pHTTPContext->BufferAvailable();
02211     }
02212 }
02213 
02214 
02215 /* Prints out the remote machine IP address and port number.
02216 
02217    Parameters:
02218     pPCB is a pointer to the TCP protocol control block.
02219     
02220    Returns:
02221     Nothing.
02222 */
02223 static void NetworkPrintRemoteAddress(tcp_pcb* pPCB)
02224 {
02225     // Validate paramters.
02226     assert ( pPCB );
02227     
02228     if (HTTP_TRACE)
02229     {
02230         SNetwork_PrintAddress(&pPCB->remote_ip);
02231         printf(":%u", pPCB->remote_port);
02232     }
02233 }