Web server based weather station using Sparkfun Weather Meters.
Dependencies: FatFileSystem mbed WeatherMeters SDFileSystem
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 }
Generated on Tue Jul 12 2022 20:48:15 by 1.7.2