Web server based weather station using Sparkfun Weather Meters.
Dependencies: FatFileSystem mbed WeatherMeters SDFileSystem
HTTPServer.cpp@1:c7958aa34fa1, 2012-02-25 (annotated)
- Committer:
- AdamGreen
- Date:
- Sat Feb 25 03:28:05 2012 +0000
- Revision:
- 1:c7958aa34fa1
- Parent:
- 0:616601bde9fb
Use published libraries where possible.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
AdamGreen | 0:616601bde9fb | 1 | /* Copyright 2011 Adam Green (http://mbed.org/users/AdamGreen/) |
AdamGreen | 0:616601bde9fb | 2 | |
AdamGreen | 0:616601bde9fb | 3 | Licensed under the Apache License, Version 2.0 (the "License"); |
AdamGreen | 0:616601bde9fb | 4 | you may not use this file except in compliance with the License. |
AdamGreen | 0:616601bde9fb | 5 | You may obtain a copy of the License at |
AdamGreen | 0:616601bde9fb | 6 | |
AdamGreen | 0:616601bde9fb | 7 | http://www.apache.org/licenses/LICENSE-2.0 |
AdamGreen | 0:616601bde9fb | 8 | |
AdamGreen | 0:616601bde9fb | 9 | Unless required by applicable law or agreed to in writing, software |
AdamGreen | 0:616601bde9fb | 10 | distributed under the License is distributed on an "AS IS" BASIS, |
AdamGreen | 0:616601bde9fb | 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
AdamGreen | 0:616601bde9fb | 12 | See the License for the specific language governing permissions and |
AdamGreen | 0:616601bde9fb | 13 | limitations under the License. |
AdamGreen | 0:616601bde9fb | 14 | */ |
AdamGreen | 0:616601bde9fb | 15 | /* Implementation of HTTP Server functionality. */ |
AdamGreen | 0:616601bde9fb | 16 | #include "HTTPServer.h" |
AdamGreen | 0:616601bde9fb | 17 | #include "network.h" |
AdamGreen | 0:616601bde9fb | 18 | #include "debug.h" |
AdamGreen | 0:616601bde9fb | 19 | |
AdamGreen | 0:616601bde9fb | 20 | |
AdamGreen | 0:616601bde9fb | 21 | // HTTP_TRACE to 1/0 to enable/disable logging for the HTTP server. |
AdamGreen | 0:616601bde9fb | 22 | #define HTTP_TRACE 0 |
AdamGreen | 0:616601bde9fb | 23 | #if HTTP_TRACE |
AdamGreen | 0:616601bde9fb | 24 | #define TRACE printf |
AdamGreen | 0:616601bde9fb | 25 | #else |
AdamGreen | 0:616601bde9fb | 26 | static void __trace(...) |
AdamGreen | 0:616601bde9fb | 27 | { |
AdamGreen | 0:616601bde9fb | 28 | return; |
AdamGreen | 0:616601bde9fb | 29 | } |
AdamGreen | 0:616601bde9fb | 30 | #define TRACE __trace |
AdamGreen | 0:616601bde9fb | 31 | #endif // HTTP_TRACE |
AdamGreen | 0:616601bde9fb | 32 | |
AdamGreen | 0:616601bde9fb | 33 | |
AdamGreen | 0:616601bde9fb | 34 | |
AdamGreen | 0:616601bde9fb | 35 | // Maximum size of buffer to be allocated on the stack for building filename |
AdamGreen | 0:616601bde9fb | 36 | // from URI that can be used in mbed fopen() call. |
AdamGreen | 0:616601bde9fb | 37 | #define HTTP_MAX_FILENAME 64 |
AdamGreen | 0:616601bde9fb | 38 | |
AdamGreen | 0:616601bde9fb | 39 | // Maximum size of a request line to be buffered. |
AdamGreen | 0:616601bde9fb | 40 | #define HTTP_MAX_REQUEST_LINE 128 |
AdamGreen | 0:616601bde9fb | 41 | |
AdamGreen | 0:616601bde9fb | 42 | |
AdamGreen | 0:616601bde9fb | 43 | // Utility macro to determine the element count of an array. |
AdamGreen | 0:616601bde9fb | 44 | #define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0])) |
AdamGreen | 0:616601bde9fb | 45 | |
AdamGreen | 0:616601bde9fb | 46 | |
AdamGreen | 0:616601bde9fb | 47 | |
AdamGreen | 0:616601bde9fb | 48 | // Default filenames to be used when the requested URI is "/" |
AdamGreen | 0:616601bde9fb | 49 | static const char* g_DefaultFilenames[] = |
AdamGreen | 0:616601bde9fb | 50 | { |
AdamGreen | 0:616601bde9fb | 51 | "/index.html", |
AdamGreen | 0:616601bde9fb | 52 | "/index.htm" |
AdamGreen | 0:616601bde9fb | 53 | }; |
AdamGreen | 0:616601bde9fb | 54 | |
AdamGreen | 0:616601bde9fb | 55 | |
AdamGreen | 0:616601bde9fb | 56 | |
AdamGreen | 0:616601bde9fb | 57 | // Static-Scope Function Prototypes. |
AdamGreen | 0:616601bde9fb | 58 | static void NetworkPrintRemoteAddress(tcp_pcb* pPCB); |
AdamGreen | 0:616601bde9fb | 59 | |
AdamGreen | 0:616601bde9fb | 60 | |
AdamGreen | 0:616601bde9fb | 61 | |
AdamGreen | 0:616601bde9fb | 62 | // Structure used to represent each header to be sent. It includes both a |
AdamGreen | 0:616601bde9fb | 63 | // pointer to the constant string and the length of the constant string so |
AdamGreen | 0:616601bde9fb | 64 | // that a memcpy() can be used for moving into buffer instead of strcpy(). |
AdamGreen | 0:616601bde9fb | 65 | struct SHeader |
AdamGreen | 0:616601bde9fb | 66 | { |
AdamGreen | 0:616601bde9fb | 67 | // Header string. |
AdamGreen | 0:616601bde9fb | 68 | const char* pHeaderString; |
AdamGreen | 0:616601bde9fb | 69 | // Length of header string, not including the NULL terminator. |
AdamGreen | 0:616601bde9fb | 70 | unsigned int HeaderLength; |
AdamGreen | 0:616601bde9fb | 71 | }; |
AdamGreen | 0:616601bde9fb | 72 | |
AdamGreen | 0:616601bde9fb | 73 | |
AdamGreen | 0:616601bde9fb | 74 | // Structure which stores information about each HTTP client connection. |
AdamGreen | 0:616601bde9fb | 75 | class CHTTPContext : protected IHTTPRequestHandlerContext |
AdamGreen | 0:616601bde9fb | 76 | { |
AdamGreen | 0:616601bde9fb | 77 | public: |
AdamGreen | 0:616601bde9fb | 78 | // Constructors/Destructors |
AdamGreen | 0:616601bde9fb | 79 | CHTTPContext(CHTTPServer* pHTTPServer, tcp_pcb* pPCB); |
AdamGreen | 0:616601bde9fb | 80 | ~CHTTPContext(); |
AdamGreen | 0:616601bde9fb | 81 | |
AdamGreen | 0:616601bde9fb | 82 | // IHTTPRequestHandlerContext methods. |
AdamGreen | 0:616601bde9fb | 83 | virtual int BeginRequestHeaders(); |
AdamGreen | 0:616601bde9fb | 84 | virtual void WriteRequestHeader(const char* pHeaderName, |
AdamGreen | 0:616601bde9fb | 85 | const char* pHeaderValue); |
AdamGreen | 0:616601bde9fb | 86 | virtual void EndRequestHeaders(); |
AdamGreen | 0:616601bde9fb | 87 | virtual int BeginRequestContent(size_t ContentSize); |
AdamGreen | 0:616601bde9fb | 88 | virtual void WriteRequestContentBlock(const void* pBlockBuffer, |
AdamGreen | 0:616601bde9fb | 89 | size_t BlockBufferSize); |
AdamGreen | 0:616601bde9fb | 90 | virtual void EndRequestContent(); |
AdamGreen | 0:616601bde9fb | 91 | virtual const char* GetStatusLine(size_t* pStatusLineLength); |
AdamGreen | 0:616601bde9fb | 92 | virtual const char* GetResponseHeaders(size_t* pResponseHeaderLength); |
AdamGreen | 0:616601bde9fb | 93 | virtual int BeginResponseContent(); |
AdamGreen | 0:616601bde9fb | 94 | virtual size_t ReadResponseContentBlock(char* pBuffer, |
AdamGreen | 0:616601bde9fb | 95 | size_t BytesToRead); |
AdamGreen | 0:616601bde9fb | 96 | virtual void EndResponseContent(); |
AdamGreen | 0:616601bde9fb | 97 | virtual void Release(); |
AdamGreen | 0:616601bde9fb | 98 | |
AdamGreen | 0:616601bde9fb | 99 | CHTTPServer* Server() { return m_pHTTPServer; } |
AdamGreen | 0:616601bde9fb | 100 | CHTTPContext* GetNext() { return m_pNext; } |
AdamGreen | 0:616601bde9fb | 101 | void RemovedFromWaitingList(CHTTPContext* pPrev) |
AdamGreen | 0:616601bde9fb | 102 | { |
AdamGreen | 0:616601bde9fb | 103 | if (pPrev) |
AdamGreen | 0:616601bde9fb | 104 | { |
AdamGreen | 0:616601bde9fb | 105 | pPrev->m_pNext = this->m_pNext; |
AdamGreen | 0:616601bde9fb | 106 | } |
AdamGreen | 0:616601bde9fb | 107 | m_pNext = NULL; |
AdamGreen | 0:616601bde9fb | 108 | m_WaitingForBuffer = 0; |
AdamGreen | 0:616601bde9fb | 109 | } |
AdamGreen | 0:616601bde9fb | 110 | void AddToWaitingList(CHTTPContext* pPrev) |
AdamGreen | 0:616601bde9fb | 111 | { |
AdamGreen | 0:616601bde9fb | 112 | if (pPrev) |
AdamGreen | 0:616601bde9fb | 113 | { |
AdamGreen | 0:616601bde9fb | 114 | assert ( !pPrev->m_pNext ); |
AdamGreen | 0:616601bde9fb | 115 | pPrev->m_pNext = this; |
AdamGreen | 0:616601bde9fb | 116 | } |
AdamGreen | 0:616601bde9fb | 117 | m_WaitingForBuffer = 1; |
AdamGreen | 0:616601bde9fb | 118 | } |
AdamGreen | 0:616601bde9fb | 119 | int IsWaitingForBuffer() { return m_WaitingForBuffer; } |
AdamGreen | 0:616601bde9fb | 120 | |
AdamGreen | 0:616601bde9fb | 121 | void CloseConnection(); |
AdamGreen | 0:616601bde9fb | 122 | void FreeContext(int ForcedFree); |
AdamGreen | 0:616601bde9fb | 123 | void BufferAvailable(); |
AdamGreen | 0:616601bde9fb | 124 | |
AdamGreen | 0:616601bde9fb | 125 | protected: |
AdamGreen | 0:616601bde9fb | 126 | // Static methods for lwIP callbacks. |
AdamGreen | 0:616601bde9fb | 127 | static err_t HTTPRecv(void* pvArg, tcp_pcb* pPCB, pbuf* pBuf, err_t err); |
AdamGreen | 0:616601bde9fb | 128 | static err_t HTTPSent(void* pvArg, tcp_pcb* pPCB, u16_t SendCount); |
AdamGreen | 0:616601bde9fb | 129 | static void HTTPError(void* pvArg, err_t Error); |
AdamGreen | 0:616601bde9fb | 130 | static err_t HTTPPoll(void* pvArg, tcp_pcb* pPCB); |
AdamGreen | 0:616601bde9fb | 131 | |
AdamGreen | 0:616601bde9fb | 132 | // Protected helper methods. |
AdamGreen | 0:616601bde9fb | 133 | void ProcessRequestData(pbuf* pBuf); |
AdamGreen | 0:616601bde9fb | 134 | void BufferRequestLines(pbuf* pBuf); |
AdamGreen | 0:616601bde9fb | 135 | void ProcessRequestContent(pbuf* pBuf); |
AdamGreen | 0:616601bde9fb | 136 | void ParseRequestLine(); |
AdamGreen | 0:616601bde9fb | 137 | void ParseFirstRequestLine(); |
AdamGreen | 0:616601bde9fb | 138 | void ParseHeaderRequestLine(); |
AdamGreen | 0:616601bde9fb | 139 | void StartRequestHandler(const char* pURI); |
AdamGreen | 0:616601bde9fb | 140 | void StartGetRequest(const char* pURI); |
AdamGreen | 0:616601bde9fb | 141 | void StartHeadRequest(const char* pURI); |
AdamGreen | 0:616601bde9fb | 142 | void StartPostRequest(const char* pURI); |
AdamGreen | 0:616601bde9fb | 143 | void StartBadRequest(const char* pRequest); |
AdamGreen | 0:616601bde9fb | 144 | void OpenFile(const char* pURI); |
AdamGreen | 0:616601bde9fb | 145 | void PrepareResponse(); |
AdamGreen | 0:616601bde9fb | 146 | void PrepareResponseHeaders(); |
AdamGreen | 0:616601bde9fb | 147 | void ReadContent(); |
AdamGreen | 0:616601bde9fb | 148 | void SendResponse(); |
AdamGreen | 0:616601bde9fb | 149 | int WriteData(); |
AdamGreen | 0:616601bde9fb | 150 | |
AdamGreen | 0:616601bde9fb | 151 | // States used to track the HTTP request parsing progress. |
AdamGreen | 0:616601bde9fb | 152 | enum EParseState { |
AdamGreen | 0:616601bde9fb | 153 | STATE_FIRST_LINE, |
AdamGreen | 0:616601bde9fb | 154 | STATE_HEADERS, |
AdamGreen | 0:616601bde9fb | 155 | STATE_CONTENT, |
AdamGreen | 0:616601bde9fb | 156 | STATE_DONE |
AdamGreen | 0:616601bde9fb | 157 | }; |
AdamGreen | 0:616601bde9fb | 158 | |
AdamGreen | 0:616601bde9fb | 159 | // Type of HTTP request that has been made by the client. |
AdamGreen | 0:616601bde9fb | 160 | enum ERequestType |
AdamGreen | 0:616601bde9fb | 161 | { |
AdamGreen | 0:616601bde9fb | 162 | TYPE_GET_0_9, |
AdamGreen | 0:616601bde9fb | 163 | TYPE_GET_1_0, |
AdamGreen | 0:616601bde9fb | 164 | TYPE_HEAD, |
AdamGreen | 0:616601bde9fb | 165 | TYPE_POST, |
AdamGreen | 0:616601bde9fb | 166 | TYPE_BAD_REQUEST |
AdamGreen | 0:616601bde9fb | 167 | }; |
AdamGreen | 0:616601bde9fb | 168 | |
AdamGreen | 0:616601bde9fb | 169 | // Pointer to parent HTTP Server object. |
AdamGreen | 0:616601bde9fb | 170 | CHTTPServer* m_pHTTPServer; |
AdamGreen | 0:616601bde9fb | 171 | // Pointer to the tcp_pcb 'socket' for this context. |
AdamGreen | 0:616601bde9fb | 172 | tcp_pcb* m_pPCB; |
AdamGreen | 0:616601bde9fb | 173 | // The next context in the buffer waiting queue. |
AdamGreen | 0:616601bde9fb | 174 | CHTTPContext* m_pNext; |
AdamGreen | 0:616601bde9fb | 175 | // Handle of file to be sent to the client. |
AdamGreen | 0:616601bde9fb | 176 | FILE* m_pFile; |
AdamGreen | 0:616601bde9fb | 177 | // Pointer to the request handler context. May point to self for |
AdamGreen | 0:616601bde9fb | 178 | // default GET/HEAD request handling. |
AdamGreen | 0:616601bde9fb | 179 | IHTTPRequestHandlerContext* m_pRequestHandlerContext; |
AdamGreen | 0:616601bde9fb | 180 | // Content-Type description header field. |
AdamGreen | 0:616601bde9fb | 181 | const char* m_pContentType; |
AdamGreen | 0:616601bde9fb | 182 | // HTML text to be sent back in response to error. |
AdamGreen | 0:616601bde9fb | 183 | const char* m_pErrorHTML; |
AdamGreen | 0:616601bde9fb | 184 | // Queue of buffers used by this context. BufferQueueHead and |
AdamGreen | 0:616601bde9fb | 185 | // BufferQueueTail track the current buffers being written and/or |
AdamGreen | 0:616601bde9fb | 186 | // waiting for acknowledgment from remote machine. |
AdamGreen | 0:616601bde9fb | 187 | SBuffer* m_BufferQueue[2]; |
AdamGreen | 0:616601bde9fb | 188 | // Array of pointers to constant header strings to be sent before response |
AdamGreen | 0:616601bde9fb | 189 | // content. Status Line, Server:, Other Headers, /r/n |
AdamGreen | 0:616601bde9fb | 190 | SHeader m_HeaderArray[4]; |
AdamGreen | 0:616601bde9fb | 191 | // The length of the m_pContentType string. |
AdamGreen | 0:616601bde9fb | 192 | size_t m_ContentTypeLength; |
AdamGreen | 0:616601bde9fb | 193 | // The length of the m_pErrorHTML string. |
AdamGreen | 0:616601bde9fb | 194 | size_t m_ErrorHTMLLength; |
AdamGreen | 0:616601bde9fb | 195 | // The current read offset into the m_pErrorHTML string. |
AdamGreen | 0:616601bde9fb | 196 | size_t m_ErrorHTMLOffset; |
AdamGreen | 0:616601bde9fb | 197 | // UNDONE: Many of these fields could be packed into a single field to save memory. |
AdamGreen | 0:616601bde9fb | 198 | // Are we waiting for a buffer already? |
AdamGreen | 0:616601bde9fb | 199 | int m_WaitingForBuffer; |
AdamGreen | 0:616601bde9fb | 200 | // Are we in the process of closing down the connection? |
AdamGreen | 0:616601bde9fb | 201 | int m_ClosingConnection; |
AdamGreen | 0:616601bde9fb | 202 | // Should request headers be sent to handler? |
AdamGreen | 0:616601bde9fb | 203 | int m_SendRequestHeadersToHandler; |
AdamGreen | 0:616601bde9fb | 204 | // Should request content be sent to handler? |
AdamGreen | 0:616601bde9fb | 205 | int m_SendRequestContentToHandler; |
AdamGreen | 0:616601bde9fb | 206 | // Content-Length of request content body. |
AdamGreen | 0:616601bde9fb | 207 | unsigned int m_RequestContentLength; |
AdamGreen | 0:616601bde9fb | 208 | // The current state of the request parsing code. |
AdamGreen | 0:616601bde9fb | 209 | EParseState m_ParseState; |
AdamGreen | 0:616601bde9fb | 210 | // The type of request being made by the client. |
AdamGreen | 0:616601bde9fb | 211 | ERequestType m_RequestType; |
AdamGreen | 0:616601bde9fb | 212 | // UNDONE: Check for memory savings across these structures. |
AdamGreen | 0:616601bde9fb | 213 | // Offset into m_LineBuffer where next character should be placed. |
AdamGreen | 0:616601bde9fb | 214 | unsigned short m_LineBufferOffset; |
AdamGreen | 0:616601bde9fb | 215 | // Offset into current header being sent to client. |
AdamGreen | 0:616601bde9fb | 216 | unsigned short m_HeaderOffset; |
AdamGreen | 0:616601bde9fb | 217 | // Head and tail indices for SentBufferQueue. |
AdamGreen | 0:616601bde9fb | 218 | // Head is the buffer currently being written. |
AdamGreen | 0:616601bde9fb | 219 | unsigned char m_BufferQueueHead; |
AdamGreen | 0:616601bde9fb | 220 | // Tail is the buffer currently in the process of being acknowledged. |
AdamGreen | 0:616601bde9fb | 221 | unsigned char m_BufferQueueTail; |
AdamGreen | 0:616601bde9fb | 222 | // Number of tcp_write() retries attempted so far. |
AdamGreen | 0:616601bde9fb | 223 | unsigned char m_RetryCount; |
AdamGreen | 0:616601bde9fb | 224 | // Current index into HeaderArray[] of next header to be sent to client. |
AdamGreen | 0:616601bde9fb | 225 | unsigned char m_HeaderIndex; |
AdamGreen | 0:616601bde9fb | 226 | // Each line of the request is buffered here. |
AdamGreen | 0:616601bde9fb | 227 | char m_LineBuffer[HTTP_MAX_REQUEST_LINE+1]; |
AdamGreen | 0:616601bde9fb | 228 | }; |
AdamGreen | 0:616601bde9fb | 229 | |
AdamGreen | 0:616601bde9fb | 230 | |
AdamGreen | 0:616601bde9fb | 231 | CHTTPContext::CHTTPContext(CHTTPServer* pHTTPServer, tcp_pcb* pPCB) |
AdamGreen | 0:616601bde9fb | 232 | { |
AdamGreen | 0:616601bde9fb | 233 | m_pHTTPServer = pHTTPServer; |
AdamGreen | 0:616601bde9fb | 234 | m_pPCB = pPCB; |
AdamGreen | 0:616601bde9fb | 235 | m_pNext = NULL; |
AdamGreen | 0:616601bde9fb | 236 | m_pFile = NULL; |
AdamGreen | 0:616601bde9fb | 237 | m_pRequestHandlerContext = NULL; |
AdamGreen | 0:616601bde9fb | 238 | m_pContentType = NULL; |
AdamGreen | 0:616601bde9fb | 239 | m_pErrorHTML = NULL; |
AdamGreen | 0:616601bde9fb | 240 | memset(m_BufferQueue, 0, sizeof(m_BufferQueue)); |
AdamGreen | 0:616601bde9fb | 241 | memset(m_HeaderArray, 0, sizeof(m_HeaderArray)); |
AdamGreen | 0:616601bde9fb | 242 | m_ContentTypeLength = 0; |
AdamGreen | 0:616601bde9fb | 243 | m_ErrorHTMLLength = 0; |
AdamGreen | 0:616601bde9fb | 244 | m_ErrorHTMLOffset = 0; |
AdamGreen | 0:616601bde9fb | 245 | m_WaitingForBuffer = 0; |
AdamGreen | 0:616601bde9fb | 246 | m_ClosingConnection = 0; |
AdamGreen | 0:616601bde9fb | 247 | m_SendRequestHeadersToHandler = 0; |
AdamGreen | 0:616601bde9fb | 248 | m_SendRequestContentToHandler = 0; |
AdamGreen | 0:616601bde9fb | 249 | m_RequestContentLength = 0; |
AdamGreen | 0:616601bde9fb | 250 | m_ParseState = STATE_FIRST_LINE; |
AdamGreen | 0:616601bde9fb | 251 | m_RequestType = TYPE_BAD_REQUEST; |
AdamGreen | 0:616601bde9fb | 252 | m_LineBufferOffset = 0; |
AdamGreen | 0:616601bde9fb | 253 | m_HeaderOffset = 0; |
AdamGreen | 0:616601bde9fb | 254 | m_BufferQueueHead = 0; |
AdamGreen | 0:616601bde9fb | 255 | m_BufferQueueTail = 0; |
AdamGreen | 0:616601bde9fb | 256 | m_RetryCount = 0; |
AdamGreen | 0:616601bde9fb | 257 | m_HeaderIndex = 0; |
AdamGreen | 0:616601bde9fb | 258 | m_LineBuffer[0] = '\0'; |
AdamGreen | 0:616601bde9fb | 259 | |
AdamGreen | 0:616601bde9fb | 260 | // Associate this context with the lwIP PCB object. |
AdamGreen | 0:616601bde9fb | 261 | tcp_arg(pPCB, this); |
AdamGreen | 0:616601bde9fb | 262 | |
AdamGreen | 0:616601bde9fb | 263 | // Setup the callbacks to be called whenever any activity occurs on this new |
AdamGreen | 0:616601bde9fb | 264 | // client PCB. |
AdamGreen | 0:616601bde9fb | 265 | tcp_recv(pPCB, HTTPRecv); |
AdamGreen | 0:616601bde9fb | 266 | tcp_sent(pPCB, HTTPSent); |
AdamGreen | 0:616601bde9fb | 267 | tcp_err(pPCB, HTTPError); |
AdamGreen | 0:616601bde9fb | 268 | |
AdamGreen | 0:616601bde9fb | 269 | // There might be scenarios where the application can't queue up more work |
AdamGreen | 0:616601bde9fb | 270 | // for this client PCB (due to things like out of memory) so that activity |
AdamGreen | 0:616601bde9fb | 271 | // could cease on this PCB when there were no more calls to functions like |
AdamGreen | 0:616601bde9fb | 272 | // HTTPSent(). To make sure this doesn't happen, have lwIP callback into |
AdamGreen | 0:616601bde9fb | 273 | // the HTTPPoll() function every 4 iterations of the coarse TCP timer (2 |
AdamGreen | 0:616601bde9fb | 274 | // seconds). HTTPPoll() can take care of retrying any previously failed |
AdamGreen | 0:616601bde9fb | 275 | // operations. |
AdamGreen | 0:616601bde9fb | 276 | tcp_poll(pPCB, HTTPPoll, 4); |
AdamGreen | 0:616601bde9fb | 277 | } |
AdamGreen | 0:616601bde9fb | 278 | |
AdamGreen | 0:616601bde9fb | 279 | |
AdamGreen | 0:616601bde9fb | 280 | // Destructor |
AdamGreen | 0:616601bde9fb | 281 | CHTTPContext::~CHTTPContext() |
AdamGreen | 0:616601bde9fb | 282 | { |
AdamGreen | 0:616601bde9fb | 283 | // Make sure that everything was cleaned up before destructor was called. |
AdamGreen | 0:616601bde9fb | 284 | assert ( NULL == m_pFile ); |
AdamGreen | 0:616601bde9fb | 285 | assert ( NULL == m_pPCB ); |
AdamGreen | 0:616601bde9fb | 286 | assert ( NULL == m_pRequestHandlerContext ); |
AdamGreen | 0:616601bde9fb | 287 | } |
AdamGreen | 0:616601bde9fb | 288 | |
AdamGreen | 0:616601bde9fb | 289 | |
AdamGreen | 0:616601bde9fb | 290 | int CHTTPContext::BeginRequestHeaders() |
AdamGreen | 0:616601bde9fb | 291 | { |
AdamGreen | 0:616601bde9fb | 292 | // This context already parses out the Content-Length header that it needs. |
AdamGreen | 0:616601bde9fb | 293 | return 0; |
AdamGreen | 0:616601bde9fb | 294 | } |
AdamGreen | 0:616601bde9fb | 295 | |
AdamGreen | 0:616601bde9fb | 296 | void CHTTPContext::WriteRequestHeader(const char* pHeaderName, |
AdamGreen | 0:616601bde9fb | 297 | const char* pHeaderValue) |
AdamGreen | 0:616601bde9fb | 298 | { |
AdamGreen | 0:616601bde9fb | 299 | static const int ShouldNotBeCalled = 0; |
AdamGreen | 0:616601bde9fb | 300 | |
AdamGreen | 0:616601bde9fb | 301 | (void)pHeaderName; |
AdamGreen | 0:616601bde9fb | 302 | (void)pHeaderValue; |
AdamGreen | 0:616601bde9fb | 303 | |
AdamGreen | 0:616601bde9fb | 304 | assert ( ShouldNotBeCalled ); |
AdamGreen | 0:616601bde9fb | 305 | } |
AdamGreen | 0:616601bde9fb | 306 | |
AdamGreen | 0:616601bde9fb | 307 | void CHTTPContext::EndRequestHeaders() |
AdamGreen | 0:616601bde9fb | 308 | { |
AdamGreen | 0:616601bde9fb | 309 | static const int ShouldNotBeCalled = 0; |
AdamGreen | 0:616601bde9fb | 310 | |
AdamGreen | 0:616601bde9fb | 311 | assert ( ShouldNotBeCalled ); |
AdamGreen | 0:616601bde9fb | 312 | } |
AdamGreen | 0:616601bde9fb | 313 | |
AdamGreen | 0:616601bde9fb | 314 | int CHTTPContext::BeginRequestContent(size_t ContentSize) |
AdamGreen | 0:616601bde9fb | 315 | { |
AdamGreen | 0:616601bde9fb | 316 | (void)ContentSize; |
AdamGreen | 0:616601bde9fb | 317 | |
AdamGreen | 0:616601bde9fb | 318 | // Only processing GET and HEAD requests so don't care about content sent |
AdamGreen | 0:616601bde9fb | 319 | // in request. |
AdamGreen | 0:616601bde9fb | 320 | return 0; |
AdamGreen | 0:616601bde9fb | 321 | } |
AdamGreen | 0:616601bde9fb | 322 | |
AdamGreen | 0:616601bde9fb | 323 | void CHTTPContext::WriteRequestContentBlock(const void* pBlockBuffer, |
AdamGreen | 0:616601bde9fb | 324 | size_t BlockBufferSize) |
AdamGreen | 0:616601bde9fb | 325 | { |
AdamGreen | 0:616601bde9fb | 326 | static const int ShouldNotBeCalled = 0; |
AdamGreen | 0:616601bde9fb | 327 | |
AdamGreen | 0:616601bde9fb | 328 | (void)pBlockBuffer; |
AdamGreen | 0:616601bde9fb | 329 | (void)BlockBufferSize; |
AdamGreen | 0:616601bde9fb | 330 | |
AdamGreen | 0:616601bde9fb | 331 | assert ( ShouldNotBeCalled ); |
AdamGreen | 0:616601bde9fb | 332 | } |
AdamGreen | 0:616601bde9fb | 333 | |
AdamGreen | 0:616601bde9fb | 334 | void CHTTPContext::EndRequestContent() |
AdamGreen | 0:616601bde9fb | 335 | { |
AdamGreen | 0:616601bde9fb | 336 | static const int ShouldNotBeCalled = 0; |
AdamGreen | 0:616601bde9fb | 337 | |
AdamGreen | 0:616601bde9fb | 338 | assert ( ShouldNotBeCalled ); |
AdamGreen | 0:616601bde9fb | 339 | } |
AdamGreen | 0:616601bde9fb | 340 | |
AdamGreen | 0:616601bde9fb | 341 | const char* CHTTPContext::GetStatusLine(size_t* pStatusLineLength) |
AdamGreen | 0:616601bde9fb | 342 | { |
AdamGreen | 0:616601bde9fb | 343 | static const char Ok[] = "HTTP/1.0 200 OK\r\n"; |
AdamGreen | 0:616601bde9fb | 344 | static const char NotFound[] = "HTTP/1.0 404 Not Found\r\n"; |
AdamGreen | 0:616601bde9fb | 345 | static const char BadRequest[] = "HTTP/1.0 400 Bad Request\r\n"; |
AdamGreen | 0:616601bde9fb | 346 | static const char NotImplemented[] = "HTTP/1.0 501 Not Implemented\r\n"; |
AdamGreen | 0:616601bde9fb | 347 | |
AdamGreen | 0:616601bde9fb | 348 | switch (m_RequestType) |
AdamGreen | 0:616601bde9fb | 349 | { |
AdamGreen | 0:616601bde9fb | 350 | case TYPE_HEAD: |
AdamGreen | 0:616601bde9fb | 351 | case TYPE_GET_1_0: |
AdamGreen | 0:616601bde9fb | 352 | if (m_pFile) |
AdamGreen | 0:616601bde9fb | 353 | { |
AdamGreen | 0:616601bde9fb | 354 | *pStatusLineLength = sizeof(Ok) - 1; |
AdamGreen | 0:616601bde9fb | 355 | return Ok; |
AdamGreen | 0:616601bde9fb | 356 | } |
AdamGreen | 0:616601bde9fb | 357 | else |
AdamGreen | 0:616601bde9fb | 358 | { |
AdamGreen | 0:616601bde9fb | 359 | *pStatusLineLength = sizeof(NotFound) - 1; |
AdamGreen | 0:616601bde9fb | 360 | return NotFound; |
AdamGreen | 0:616601bde9fb | 361 | } |
AdamGreen | 0:616601bde9fb | 362 | break; |
AdamGreen | 0:616601bde9fb | 363 | case TYPE_GET_0_9: |
AdamGreen | 0:616601bde9fb | 364 | // Shouldn't get called for such a request. |
AdamGreen | 0:616601bde9fb | 365 | assert ( TYPE_GET_0_9 != m_RequestType ); |
AdamGreen | 0:616601bde9fb | 366 | return NULL; |
AdamGreen | 0:616601bde9fb | 367 | case TYPE_BAD_REQUEST: |
AdamGreen | 0:616601bde9fb | 368 | *pStatusLineLength = sizeof(BadRequest) - 1; |
AdamGreen | 0:616601bde9fb | 369 | return BadRequest; |
AdamGreen | 0:616601bde9fb | 370 | default: |
AdamGreen | 0:616601bde9fb | 371 | // This server has no default handling for any other type of request. |
AdamGreen | 0:616601bde9fb | 372 | *pStatusLineLength = sizeof(NotImplemented) - 1; |
AdamGreen | 0:616601bde9fb | 373 | return NotImplemented; |
AdamGreen | 0:616601bde9fb | 374 | } |
AdamGreen | 0:616601bde9fb | 375 | } |
AdamGreen | 0:616601bde9fb | 376 | |
AdamGreen | 0:616601bde9fb | 377 | const char* CHTTPContext::GetResponseHeaders(size_t* pResponseHeaderLength) |
AdamGreen | 0:616601bde9fb | 378 | { |
AdamGreen | 0:616601bde9fb | 379 | // Additional error text will be returned as HTML text. |
AdamGreen | 0:616601bde9fb | 380 | static const char ContentTypeHTML[] = "Content-type: text/html\r\n"; |
AdamGreen | 0:616601bde9fb | 381 | static const char NotImplementedHTML[] = "<html>" |
AdamGreen | 0:616601bde9fb | 382 | "<body><h2>501: Not Implemented.</h2></body>" |
AdamGreen | 0:616601bde9fb | 383 | "</html>\r\n"; |
AdamGreen | 0:616601bde9fb | 384 | static const char BadRequestHTML[] = "<html>" |
AdamGreen | 0:616601bde9fb | 385 | "<body><h2>400: Bad Request.</h2></body>" |
AdamGreen | 0:616601bde9fb | 386 | "</html>\r\n"; |
AdamGreen | 0:616601bde9fb | 387 | |
AdamGreen | 0:616601bde9fb | 388 | switch (m_RequestType) |
AdamGreen | 0:616601bde9fb | 389 | { |
AdamGreen | 0:616601bde9fb | 390 | case TYPE_HEAD: |
AdamGreen | 0:616601bde9fb | 391 | case TYPE_GET_1_0: |
AdamGreen | 0:616601bde9fb | 392 | *pResponseHeaderLength = m_ContentTypeLength; |
AdamGreen | 0:616601bde9fb | 393 | return m_pContentType; |
AdamGreen | 0:616601bde9fb | 394 | case TYPE_GET_0_9: |
AdamGreen | 0:616601bde9fb | 395 | // Shouldn't get called for such a request. |
AdamGreen | 0:616601bde9fb | 396 | assert ( TYPE_GET_0_9 != m_RequestType ); |
AdamGreen | 0:616601bde9fb | 397 | return NULL; |
AdamGreen | 0:616601bde9fb | 398 | case TYPE_BAD_REQUEST: |
AdamGreen | 0:616601bde9fb | 399 | // Will send error back as HTML text. |
AdamGreen | 0:616601bde9fb | 400 | m_pErrorHTML = BadRequestHTML; |
AdamGreen | 0:616601bde9fb | 401 | m_ErrorHTMLLength = sizeof(BadRequestHTML) - 1; |
AdamGreen | 0:616601bde9fb | 402 | *pResponseHeaderLength = sizeof(ContentTypeHTML) - 1; |
AdamGreen | 0:616601bde9fb | 403 | return ContentTypeHTML; |
AdamGreen | 0:616601bde9fb | 404 | default: |
AdamGreen | 0:616601bde9fb | 405 | // This server has no default handling for any other type of request. |
AdamGreen | 0:616601bde9fb | 406 | m_pErrorHTML = NotImplementedHTML; |
AdamGreen | 0:616601bde9fb | 407 | m_ErrorHTMLLength = sizeof(NotImplementedHTML) - 1; |
AdamGreen | 0:616601bde9fb | 408 | *pResponseHeaderLength = sizeof(ContentTypeHTML) - 1; |
AdamGreen | 0:616601bde9fb | 409 | return ContentTypeHTML; |
AdamGreen | 0:616601bde9fb | 410 | } |
AdamGreen | 0:616601bde9fb | 411 | } |
AdamGreen | 0:616601bde9fb | 412 | |
AdamGreen | 0:616601bde9fb | 413 | int CHTTPContext::BeginResponseContent() |
AdamGreen | 0:616601bde9fb | 414 | { |
AdamGreen | 0:616601bde9fb | 415 | // Have response content to send back so return 1; |
AdamGreen | 0:616601bde9fb | 416 | assert ( m_pFile || m_pErrorHTML ); |
AdamGreen | 0:616601bde9fb | 417 | return 1; |
AdamGreen | 0:616601bde9fb | 418 | } |
AdamGreen | 0:616601bde9fb | 419 | |
AdamGreen | 0:616601bde9fb | 420 | size_t CHTTPContext::ReadResponseContentBlock(char* pBuffer, |
AdamGreen | 0:616601bde9fb | 421 | size_t BytesToRead) |
AdamGreen | 0:616601bde9fb | 422 | { |
AdamGreen | 0:616601bde9fb | 423 | if (m_pFile) |
AdamGreen | 0:616601bde9fb | 424 | { |
AdamGreen | 0:616601bde9fb | 425 | // Read content from file. |
AdamGreen | 0:616601bde9fb | 426 | return fread(pBuffer, 1, BytesToRead, m_pFile); |
AdamGreen | 0:616601bde9fb | 427 | } |
AdamGreen | 0:616601bde9fb | 428 | else |
AdamGreen | 0:616601bde9fb | 429 | { |
AdamGreen | 0:616601bde9fb | 430 | // Read content from HTML text for error. |
AdamGreen | 0:616601bde9fb | 431 | size_t BytesLeft = m_ErrorHTMLLength - m_ErrorHTMLOffset; |
AdamGreen | 0:616601bde9fb | 432 | |
AdamGreen | 0:616601bde9fb | 433 | if (BytesToRead > BytesLeft) |
AdamGreen | 0:616601bde9fb | 434 | { |
AdamGreen | 0:616601bde9fb | 435 | BytesToRead = BytesLeft; |
AdamGreen | 0:616601bde9fb | 436 | } |
AdamGreen | 0:616601bde9fb | 437 | memcpy(pBuffer, &m_pErrorHTML[m_ErrorHTMLOffset], BytesToRead); |
AdamGreen | 0:616601bde9fb | 438 | m_ErrorHTMLOffset += BytesToRead; |
AdamGreen | 0:616601bde9fb | 439 | |
AdamGreen | 0:616601bde9fb | 440 | return BytesToRead; |
AdamGreen | 0:616601bde9fb | 441 | } |
AdamGreen | 0:616601bde9fb | 442 | } |
AdamGreen | 0:616601bde9fb | 443 | |
AdamGreen | 0:616601bde9fb | 444 | void CHTTPContext::EndResponseContent() |
AdamGreen | 0:616601bde9fb | 445 | { |
AdamGreen | 0:616601bde9fb | 446 | if (m_pFile) |
AdamGreen | 0:616601bde9fb | 447 | { |
AdamGreen | 0:616601bde9fb | 448 | fclose(m_pFile); |
AdamGreen | 0:616601bde9fb | 449 | m_pFile = NULL; |
AdamGreen | 0:616601bde9fb | 450 | } |
AdamGreen | 0:616601bde9fb | 451 | } |
AdamGreen | 0:616601bde9fb | 452 | |
AdamGreen | 0:616601bde9fb | 453 | void CHTTPContext::Release() |
AdamGreen | 0:616601bde9fb | 454 | { |
AdamGreen | 0:616601bde9fb | 455 | // Just make sure that any files are closed. |
AdamGreen | 0:616601bde9fb | 456 | EndResponseContent(); |
AdamGreen | 0:616601bde9fb | 457 | |
AdamGreen | 0:616601bde9fb | 458 | return; |
AdamGreen | 0:616601bde9fb | 459 | } |
AdamGreen | 0:616601bde9fb | 460 | |
AdamGreen | 0:616601bde9fb | 461 | |
AdamGreen | 0:616601bde9fb | 462 | /* Called by lwIP whenever data is sent to an active connection. The main task |
AdamGreen | 0:616601bde9fb | 463 | of this callback is to acknowledge application receipt of the data, parse |
AdamGreen | 0:616601bde9fb | 464 | the received data to determine which file is being requested by the client, |
AdamGreen | 0:616601bde9fb | 465 | open the requested file, and start sending it back to the remote client. |
AdamGreen | 0:616601bde9fb | 466 | |
AdamGreen | 0:616601bde9fb | 467 | Parameters: |
AdamGreen | 0:616601bde9fb | 468 | pvArg is the value set on the active PCB through the call to tcp_arg in |
AdamGreen | 0:616601bde9fb | 469 | HTTPAccept() which is a pointer to the HTTP context for this |
AdamGreen | 0:616601bde9fb | 470 | connection. |
AdamGreen | 0:616601bde9fb | 471 | pPCB is a pointer to the PCB that has just received some data from the |
AdamGreen | 0:616601bde9fb | 472 | client. |
AdamGreen | 0:616601bde9fb | 473 | pBuf is a pointer to a PBUF which contains a linked list of the data |
AdamGreen | 0:616601bde9fb | 474 | received from the remote client. |
AdamGreen | 0:616601bde9fb | 475 | err is passed in from the lwIP stack to indicate if an error occurred. |
AdamGreen | 0:616601bde9fb | 476 | |
AdamGreen | 0:616601bde9fb | 477 | Returns: |
AdamGreen | 0:616601bde9fb | 478 | ERR_ABRT if we end up aborting the connection and ERR_OK otherwise. |
AdamGreen | 0:616601bde9fb | 479 | */ |
AdamGreen | 0:616601bde9fb | 480 | err_t CHTTPContext::HTTPRecv(void* pvArg, tcp_pcb* pPCB, pbuf* pBuf, err_t err) |
AdamGreen | 0:616601bde9fb | 481 | { |
AdamGreen | 0:616601bde9fb | 482 | CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg; |
AdamGreen | 0:616601bde9fb | 483 | |
AdamGreen | 0:616601bde9fb | 484 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 485 | assert ( pPCB ); |
AdamGreen | 0:616601bde9fb | 486 | assert ( ERR_OK == err ); |
AdamGreen | 0:616601bde9fb | 487 | |
AdamGreen | 0:616601bde9fb | 488 | // If the client has closed their end then pBuf will be NULL so the server |
AdamGreen | 0:616601bde9fb | 489 | // should shutdown its end as well. |
AdamGreen | 0:616601bde9fb | 490 | if (!pBuf) |
AdamGreen | 0:616601bde9fb | 491 | { |
AdamGreen | 0:616601bde9fb | 492 | TRACE("HTTP: Closing as client closed its end for connection: "); |
AdamGreen | 0:616601bde9fb | 493 | NetworkPrintRemoteAddress(pPCB); |
AdamGreen | 0:616601bde9fb | 494 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 495 | |
AdamGreen | 0:616601bde9fb | 496 | pHTTPContext->CloseConnection(); |
AdamGreen | 0:616601bde9fb | 497 | pHTTPContext->FreeContext(TRUE); |
AdamGreen | 0:616601bde9fb | 498 | |
AdamGreen | 0:616601bde9fb | 499 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 500 | } |
AdamGreen | 0:616601bde9fb | 501 | |
AdamGreen | 0:616601bde9fb | 502 | // Let lwIP know that the HTTP application has received the data. |
AdamGreen | 0:616601bde9fb | 503 | tcp_recved(pPCB, pBuf->tot_len); |
AdamGreen | 0:616601bde9fb | 504 | |
AdamGreen | 0:616601bde9fb | 505 | // Just return if we have already freed the client context from a close |
AdamGreen | 0:616601bde9fb | 506 | // attempt. |
AdamGreen | 0:616601bde9fb | 507 | if (!pvArg) |
AdamGreen | 0:616601bde9fb | 508 | { |
AdamGreen | 0:616601bde9fb | 509 | TRACE("HTTP: Ignoring HTTPRecv() as client context was already freed for: "); |
AdamGreen | 0:616601bde9fb | 510 | NetworkPrintRemoteAddress(pPCB); |
AdamGreen | 0:616601bde9fb | 511 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 512 | goto Error; |
AdamGreen | 0:616601bde9fb | 513 | } |
AdamGreen | 0:616601bde9fb | 514 | |
AdamGreen | 0:616601bde9fb | 515 | // The PCB shouldn't change out underneath this object! |
AdamGreen | 0:616601bde9fb | 516 | assert ( pPCB == pHTTPContext->m_pPCB ); |
AdamGreen | 0:616601bde9fb | 517 | |
AdamGreen | 0:616601bde9fb | 518 | pHTTPContext->ProcessRequestData(pBuf); |
AdamGreen | 0:616601bde9fb | 519 | |
AdamGreen | 0:616601bde9fb | 520 | Error: |
AdamGreen | 0:616601bde9fb | 521 | // Release the pbuf as the application no longer requires any of its data |
AdamGreen | 0:616601bde9fb | 522 | pbuf_free(pBuf); |
AdamGreen | 0:616601bde9fb | 523 | |
AdamGreen | 0:616601bde9fb | 524 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 525 | } |
AdamGreen | 0:616601bde9fb | 526 | |
AdamGreen | 0:616601bde9fb | 527 | |
AdamGreen | 0:616601bde9fb | 528 | /* Called by lwIP whenever sent data is acknowledged by the remote machine. |
AdamGreen | 0:616601bde9fb | 529 | The main task of this callback is to update the byte count of unacknowledged |
AdamGreen | 0:616601bde9fb | 530 | bytes and send more data once the previously sent data has been acknowledged. |
AdamGreen | 0:616601bde9fb | 531 | |
AdamGreen | 0:616601bde9fb | 532 | Parameters: |
AdamGreen | 0:616601bde9fb | 533 | pvArg is the value set on the active PCB through the call to tcp_arg in |
AdamGreen | 0:616601bde9fb | 534 | HTTPAccept() which is a pointer to the HTTP context for this |
AdamGreen | 0:616601bde9fb | 535 | connection. |
AdamGreen | 0:616601bde9fb | 536 | pPCB is a pointer to the PCB that has just received acknowledgement from |
AdamGreen | 0:616601bde9fb | 537 | the remote client of sent data. |
AdamGreen | 0:616601bde9fb | 538 | AcknowledgeCount is the number of bytes that the remote client has |
AdamGreen | 0:616601bde9fb | 539 | acknowledged receipt of. |
AdamGreen | 0:616601bde9fb | 540 | |
AdamGreen | 0:616601bde9fb | 541 | Returns: |
AdamGreen | 0:616601bde9fb | 542 | ERR_ABRT if we end up aborting the connection and ERR_OK otherwise. |
AdamGreen | 0:616601bde9fb | 543 | */ |
AdamGreen | 0:616601bde9fb | 544 | err_t CHTTPContext::HTTPSent(void* pvArg, tcp_pcb* pPCB, u16_t AcknowledgeCount) |
AdamGreen | 0:616601bde9fb | 545 | { |
AdamGreen | 0:616601bde9fb | 546 | CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg; |
AdamGreen | 0:616601bde9fb | 547 | |
AdamGreen | 0:616601bde9fb | 548 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 549 | assert ( pvArg ); |
AdamGreen | 0:616601bde9fb | 550 | assert ( pPCB ); |
AdamGreen | 0:616601bde9fb | 551 | assert ( AcknowledgeCount ); |
AdamGreen | 0:616601bde9fb | 552 | |
AdamGreen | 0:616601bde9fb | 553 | // Loop through all of the buffers in the sent state to update their |
AdamGreen | 0:616601bde9fb | 554 | // unacknowledged count since this acknowledgment can span buffers. |
AdamGreen | 0:616601bde9fb | 555 | while (AcknowledgeCount > 0) |
AdamGreen | 0:616601bde9fb | 556 | { |
AdamGreen | 0:616601bde9fb | 557 | SBuffer* pSentBuffer = pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail]; |
AdamGreen | 0:616601bde9fb | 558 | |
AdamGreen | 0:616601bde9fb | 559 | // We should have a buffer in the sent state if we are getting back |
AdamGreen | 0:616601bde9fb | 560 | // acknowledgments from the remote machine. |
AdamGreen | 0:616601bde9fb | 561 | assert ( pSentBuffer ); |
AdamGreen | 0:616601bde9fb | 562 | |
AdamGreen | 0:616601bde9fb | 563 | // Update outstanding byte count for this buffer. |
AdamGreen | 0:616601bde9fb | 564 | if (AcknowledgeCount >= pSentBuffer->UnacknowledgedBytes) |
AdamGreen | 0:616601bde9fb | 565 | { |
AdamGreen | 0:616601bde9fb | 566 | // This callback acknowledges all of the data in the buffer at the |
AdamGreen | 0:616601bde9fb | 567 | // tail of the queue and possibly spans into the next buffer as |
AdamGreen | 0:616601bde9fb | 568 | // well. It is also possible that there is still more data to be |
AdamGreen | 0:616601bde9fb | 569 | // written from this buffer. |
AdamGreen | 0:616601bde9fb | 570 | AcknowledgeCount -= pSentBuffer->UnacknowledgedBytes; |
AdamGreen | 0:616601bde9fb | 571 | pSentBuffer->UnacknowledgedBytes = 0; |
AdamGreen | 0:616601bde9fb | 572 | |
AdamGreen | 0:616601bde9fb | 573 | if (0 == pSentBuffer->BytesToWrite) |
AdamGreen | 0:616601bde9fb | 574 | { |
AdamGreen | 0:616601bde9fb | 575 | // Free the buffer which might cause another client context to be |
AdamGreen | 0:616601bde9fb | 576 | // invoked if it was waiting for a buffer. |
AdamGreen | 0:616601bde9fb | 577 | pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail] = NULL; |
AdamGreen | 0:616601bde9fb | 578 | pHTTPContext->m_BufferQueueTail = (pHTTPContext->m_BufferQueueTail + 1) & 1; |
AdamGreen | 0:616601bde9fb | 579 | pHTTPContext->m_pHTTPServer->FreeBuffer(pSentBuffer); |
AdamGreen | 0:616601bde9fb | 580 | |
AdamGreen | 0:616601bde9fb | 581 | // Check to see if this context is in the closing state and all sent |
AdamGreen | 0:616601bde9fb | 582 | // buffers are now free. |
AdamGreen | 0:616601bde9fb | 583 | if (pHTTPContext->m_ClosingConnection && |
AdamGreen | 0:616601bde9fb | 584 | !pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail]) |
AdamGreen | 0:616601bde9fb | 585 | { |
AdamGreen | 0:616601bde9fb | 586 | // Free the context and return immediately since the context is |
AdamGreen | 0:616601bde9fb | 587 | // no longer valid. |
AdamGreen | 0:616601bde9fb | 588 | assert ( !pHTTPContext->m_BufferQueue[0] ); |
AdamGreen | 0:616601bde9fb | 589 | assert ( !pHTTPContext->m_BufferQueue[1] ); |
AdamGreen | 0:616601bde9fb | 590 | assert ( !pHTTPContext->m_WaitingForBuffer ); |
AdamGreen | 0:616601bde9fb | 591 | |
AdamGreen | 0:616601bde9fb | 592 | pHTTPContext->FreeContext(FALSE); |
AdamGreen | 0:616601bde9fb | 593 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 594 | } |
AdamGreen | 0:616601bde9fb | 595 | } |
AdamGreen | 0:616601bde9fb | 596 | else |
AdamGreen | 0:616601bde9fb | 597 | { |
AdamGreen | 0:616601bde9fb | 598 | // We still have data to write from this buffer so there should |
AdamGreen | 0:616601bde9fb | 599 | // no more bytes to acknowledge in another buffer. |
AdamGreen | 0:616601bde9fb | 600 | assert ( 0 == AcknowledgeCount ); |
AdamGreen | 0:616601bde9fb | 601 | } |
AdamGreen | 0:616601bde9fb | 602 | } |
AdamGreen | 0:616601bde9fb | 603 | else |
AdamGreen | 0:616601bde9fb | 604 | { |
AdamGreen | 0:616601bde9fb | 605 | // This callback is just acknowledging a portion of the data in |
AdamGreen | 0:616601bde9fb | 606 | // this buffer. |
AdamGreen | 0:616601bde9fb | 607 | pSentBuffer->UnacknowledgedBytes -= AcknowledgeCount; |
AdamGreen | 0:616601bde9fb | 608 | AcknowledgeCount = 0; |
AdamGreen | 0:616601bde9fb | 609 | } |
AdamGreen | 0:616601bde9fb | 610 | } |
AdamGreen | 0:616601bde9fb | 611 | |
AdamGreen | 0:616601bde9fb | 612 | // Attempt to send more response data to this client. |
AdamGreen | 0:616601bde9fb | 613 | pHTTPContext->SendResponse(); |
AdamGreen | 0:616601bde9fb | 614 | |
AdamGreen | 0:616601bde9fb | 615 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 616 | } |
AdamGreen | 0:616601bde9fb | 617 | |
AdamGreen | 0:616601bde9fb | 618 | |
AdamGreen | 0:616601bde9fb | 619 | /* Called by lwIP every 2 seconds (4 iterations of the tcp_slowtmr) as |
AdamGreen | 0:616601bde9fb | 620 | specified in the tcp_poll() call in HTTPInit(). Can be used to retry |
AdamGreen | 0:616601bde9fb | 621 | sending of response data or closing of the PCB. |
AdamGreen | 0:616601bde9fb | 622 | |
AdamGreen | 0:616601bde9fb | 623 | Parameters: |
AdamGreen | 0:616601bde9fb | 624 | pvArg is the value set on the active PCB through the call to tcp_arg in |
AdamGreen | 0:616601bde9fb | 625 | HTTPAccept() which is a pointer to the HTTP context for this |
AdamGreen | 0:616601bde9fb | 626 | connection. It will be NULL if we have previously failed to close |
AdamGreen | 0:616601bde9fb | 627 | the PCB. |
AdamGreen | 0:616601bde9fb | 628 | pPCB is a pointer to the low level lwIP API socket object for this |
AdamGreen | 0:616601bde9fb | 629 | connection. |
AdamGreen | 0:616601bde9fb | 630 | |
AdamGreen | 0:616601bde9fb | 631 | Returns: |
AdamGreen | 0:616601bde9fb | 632 | ERR_ABRT if we end up aborting the connection and ERR_OK otherwise. |
AdamGreen | 0:616601bde9fb | 633 | */ |
AdamGreen | 0:616601bde9fb | 634 | err_t CHTTPContext::HTTPPoll(void* pvArg, tcp_pcb* pPCB) |
AdamGreen | 0:616601bde9fb | 635 | { |
AdamGreen | 0:616601bde9fb | 636 | CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg; |
AdamGreen | 0:616601bde9fb | 637 | |
AdamGreen | 0:616601bde9fb | 638 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 639 | assert ( pPCB ); |
AdamGreen | 0:616601bde9fb | 640 | |
AdamGreen | 0:616601bde9fb | 641 | // Check to see if we need to retry closing the PCB. |
AdamGreen | 0:616601bde9fb | 642 | if (!pHTTPContext) |
AdamGreen | 0:616601bde9fb | 643 | { |
AdamGreen | 0:616601bde9fb | 644 | err_t CloseResult; |
AdamGreen | 0:616601bde9fb | 645 | |
AdamGreen | 0:616601bde9fb | 646 | TRACE("HTTP: Retrying close for connection to: "); |
AdamGreen | 0:616601bde9fb | 647 | NetworkPrintRemoteAddress(pPCB); |
AdamGreen | 0:616601bde9fb | 648 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 649 | |
AdamGreen | 0:616601bde9fb | 650 | CloseResult = tcp_close(pPCB); |
AdamGreen | 0:616601bde9fb | 651 | if (ERR_OK != CloseResult) |
AdamGreen | 0:616601bde9fb | 652 | { |
AdamGreen | 0:616601bde9fb | 653 | // This is the second failed attempt to close the PCB, force an |
AdamGreen | 0:616601bde9fb | 654 | // abort. |
AdamGreen | 0:616601bde9fb | 655 | tcp_abort(pPCB); |
AdamGreen | 0:616601bde9fb | 656 | return ERR_ABRT; |
AdamGreen | 0:616601bde9fb | 657 | } |
AdamGreen | 0:616601bde9fb | 658 | else |
AdamGreen | 0:616601bde9fb | 659 | { |
AdamGreen | 0:616601bde9fb | 660 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 661 | } |
AdamGreen | 0:616601bde9fb | 662 | } |
AdamGreen | 0:616601bde9fb | 663 | |
AdamGreen | 0:616601bde9fb | 664 | // Close the connection if the client hasn't sent its request within the polling interval. |
AdamGreen | 0:616601bde9fb | 665 | if (STATE_DONE != pHTTPContext->m_ParseState) |
AdamGreen | 0:616601bde9fb | 666 | { |
AdamGreen | 0:616601bde9fb | 667 | TRACE("HTTP: Closing connection due to no HTTP request from connection to: "); |
AdamGreen | 0:616601bde9fb | 668 | NetworkPrintRemoteAddress(pPCB); |
AdamGreen | 0:616601bde9fb | 669 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 670 | |
AdamGreen | 0:616601bde9fb | 671 | pHTTPContext->CloseConnection(); |
AdamGreen | 0:616601bde9fb | 672 | pHTTPContext->FreeContext(TRUE); |
AdamGreen | 0:616601bde9fb | 673 | |
AdamGreen | 0:616601bde9fb | 674 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 675 | } |
AdamGreen | 0:616601bde9fb | 676 | |
AdamGreen | 0:616601bde9fb | 677 | // If we have unacknowledged bytes outstanding then we will still |
AdamGreen | 0:616601bde9fb | 678 | // receive a HTTPSent() callback from lwIP so no need to retry data sends |
AdamGreen | 0:616601bde9fb | 679 | // from here. |
AdamGreen | 0:616601bde9fb | 680 | // NOTE: This version will wait forever for a request from the client. |
AdamGreen | 0:616601bde9fb | 681 | if (!pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueTail] && |
AdamGreen | 0:616601bde9fb | 682 | pHTTPContext->m_BufferQueue[pHTTPContext->m_BufferQueueHead]) |
AdamGreen | 0:616601bde9fb | 683 | { |
AdamGreen | 0:616601bde9fb | 684 | if (pHTTPContext->m_RetryCount++ > 8) |
AdamGreen | 0:616601bde9fb | 685 | { |
AdamGreen | 0:616601bde9fb | 686 | // We have already retried sending the data 8 times, so close |
AdamGreen | 0:616601bde9fb | 687 | // the connection. |
AdamGreen | 0:616601bde9fb | 688 | TRACE("HTTP: Failed multiple retries to send data for connection to: "); |
AdamGreen | 0:616601bde9fb | 689 | NetworkPrintRemoteAddress(pPCB); |
AdamGreen | 0:616601bde9fb | 690 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 691 | |
AdamGreen | 0:616601bde9fb | 692 | pHTTPContext->CloseConnection(); |
AdamGreen | 0:616601bde9fb | 693 | } |
AdamGreen | 0:616601bde9fb | 694 | else |
AdamGreen | 0:616601bde9fb | 695 | { |
AdamGreen | 0:616601bde9fb | 696 | // Retry sending the response data again. |
AdamGreen | 0:616601bde9fb | 697 | TRACE("HTTP: Retrying to send data for connection to: "); |
AdamGreen | 0:616601bde9fb | 698 | NetworkPrintRemoteAddress(pPCB); |
AdamGreen | 0:616601bde9fb | 699 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 700 | |
AdamGreen | 0:616601bde9fb | 701 | pHTTPContext->SendResponse(); |
AdamGreen | 0:616601bde9fb | 702 | } |
AdamGreen | 0:616601bde9fb | 703 | } |
AdamGreen | 0:616601bde9fb | 704 | |
AdamGreen | 0:616601bde9fb | 705 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 706 | } |
AdamGreen | 0:616601bde9fb | 707 | |
AdamGreen | 0:616601bde9fb | 708 | |
AdamGreen | 0:616601bde9fb | 709 | /* Called by lwIP whenever the PCB is closed or reset by the remote client. |
AdamGreen | 0:616601bde9fb | 710 | Gives the application the opportunity to free up the context object. |
AdamGreen | 0:616601bde9fb | 711 | |
AdamGreen | 0:616601bde9fb | 712 | Parameters: |
AdamGreen | 0:616601bde9fb | 713 | pvArg is the value set on the active PCB through the call to tcp_arg in |
AdamGreen | 0:616601bde9fb | 714 | HTTPAccept() which is a pointer to the HTTP context for this |
AdamGreen | 0:616601bde9fb | 715 | connection. |
AdamGreen | 0:616601bde9fb | 716 | Error is the error that caused the connection to be shutdown. It can be |
AdamGreen | 0:616601bde9fb | 717 | set to ERR_ABRT or ERR_RST. |
AdamGreen | 0:616601bde9fb | 718 | |
AdamGreen | 0:616601bde9fb | 719 | Returns: |
AdamGreen | 0:616601bde9fb | 720 | Nothing. |
AdamGreen | 0:616601bde9fb | 721 | */ |
AdamGreen | 0:616601bde9fb | 722 | void CHTTPContext::HTTPError(void* pvArg, err_t Error) |
AdamGreen | 0:616601bde9fb | 723 | { |
AdamGreen | 0:616601bde9fb | 724 | CHTTPContext* pHTTPContext = (CHTTPContext*)pvArg; |
AdamGreen | 0:616601bde9fb | 725 | |
AdamGreen | 0:616601bde9fb | 726 | // Unused parameters. |
AdamGreen | 0:616601bde9fb | 727 | (void)Error; |
AdamGreen | 0:616601bde9fb | 728 | |
AdamGreen | 0:616601bde9fb | 729 | TRACE("HTTP: Forcing connection context to be freed.\r\n"); |
AdamGreen | 0:616601bde9fb | 730 | |
AdamGreen | 0:616601bde9fb | 731 | // Free the context if it is non-NULL. |
AdamGreen | 0:616601bde9fb | 732 | if (pHTTPContext) |
AdamGreen | 0:616601bde9fb | 733 | { |
AdamGreen | 0:616601bde9fb | 734 | pHTTPContext->FreeContext(TRUE); |
AdamGreen | 0:616601bde9fb | 735 | } |
AdamGreen | 0:616601bde9fb | 736 | } |
AdamGreen | 0:616601bde9fb | 737 | |
AdamGreen | 0:616601bde9fb | 738 | |
AdamGreen | 0:616601bde9fb | 739 | /* Attempts to close the PCB connection. The context is placed into a closing |
AdamGreen | 0:616601bde9fb | 740 | state where it waits for the data written to be acknowledged by the remote |
AdamGreen | 0:616601bde9fb | 741 | machine before freeing up its memory. |
AdamGreen | 0:616601bde9fb | 742 | |
AdamGreen | 0:616601bde9fb | 743 | Parameters: |
AdamGreen | 0:616601bde9fb | 744 | None. |
AdamGreen | 0:616601bde9fb | 745 | Returns: |
AdamGreen | 0:616601bde9fb | 746 | Nothing. |
AdamGreen | 0:616601bde9fb | 747 | */ |
AdamGreen | 0:616601bde9fb | 748 | void CHTTPContext::CloseConnection() |
AdamGreen | 0:616601bde9fb | 749 | { |
AdamGreen | 0:616601bde9fb | 750 | err_t CloseResult = ERR_MEM; |
AdamGreen | 0:616601bde9fb | 751 | |
AdamGreen | 0:616601bde9fb | 752 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 753 | assert ( m_pPCB ); |
AdamGreen | 0:616601bde9fb | 754 | |
AdamGreen | 0:616601bde9fb | 755 | TRACE("HTTP: Closing connection to: "); |
AdamGreen | 0:616601bde9fb | 756 | NetworkPrintRemoteAddress(m_pPCB); |
AdamGreen | 0:616601bde9fb | 757 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 758 | |
AdamGreen | 0:616601bde9fb | 759 | // Flag the context as being in the closing state. |
AdamGreen | 0:616601bde9fb | 760 | m_ClosingConnection = 1; |
AdamGreen | 0:616601bde9fb | 761 | |
AdamGreen | 0:616601bde9fb | 762 | // Have no more data to send so no need for poll callback unless the |
AdamGreen | 0:616601bde9fb | 763 | // tcp_close() below should fail and require retrying. |
AdamGreen | 0:616601bde9fb | 764 | tcp_poll(m_pPCB, NULL, 0); |
AdamGreen | 0:616601bde9fb | 765 | |
AdamGreen | 0:616601bde9fb | 766 | // Atempt to close the connection. |
AdamGreen | 0:616601bde9fb | 767 | CloseResult = tcp_close(m_pPCB); |
AdamGreen | 0:616601bde9fb | 768 | if (ERR_OK != CloseResult) |
AdamGreen | 0:616601bde9fb | 769 | { |
AdamGreen | 0:616601bde9fb | 770 | // Close wasn't successful so re-enable the HTTPPoll() callback. |
AdamGreen | 0:616601bde9fb | 771 | tcp_poll(m_pPCB, HTTPPoll, 4); |
AdamGreen | 0:616601bde9fb | 772 | } |
AdamGreen | 0:616601bde9fb | 773 | } |
AdamGreen | 0:616601bde9fb | 774 | |
AdamGreen | 0:616601bde9fb | 775 | |
AdamGreen | 0:616601bde9fb | 776 | /* Frees up the resources used by the context. |
AdamGreen | 0:616601bde9fb | 777 | |
AdamGreen | 0:616601bde9fb | 778 | Parameters: |
AdamGreen | 0:616601bde9fb | 779 | ForcedFree indicates whether a shutdown is being forced and therefore it is |
AdamGreen | 0:616601bde9fb | 780 | OK to free the buffers even though they might indicate they still have |
AdamGreen | 0:616601bde9fb | 781 | outstanding data. |
AdamGreen | 0:616601bde9fb | 782 | |
AdamGreen | 0:616601bde9fb | 783 | Returns: |
AdamGreen | 0:616601bde9fb | 784 | Nothing. |
AdamGreen | 0:616601bde9fb | 785 | */ |
AdamGreen | 0:616601bde9fb | 786 | void CHTTPContext::FreeContext(int ForcedFree) |
AdamGreen | 0:616601bde9fb | 787 | { |
AdamGreen | 0:616601bde9fb | 788 | size_t i; |
AdamGreen | 0:616601bde9fb | 789 | |
AdamGreen | 0:616601bde9fb | 790 | if (m_pPCB) |
AdamGreen | 0:616601bde9fb | 791 | { |
AdamGreen | 0:616601bde9fb | 792 | TRACE("HTTP: Freeing context for connection to: "); |
AdamGreen | 0:616601bde9fb | 793 | NetworkPrintRemoteAddress(m_pPCB); |
AdamGreen | 0:616601bde9fb | 794 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 795 | } |
AdamGreen | 0:616601bde9fb | 796 | else |
AdamGreen | 0:616601bde9fb | 797 | { |
AdamGreen | 0:616601bde9fb | 798 | TRACE("HTTP: Freeing connection due to unexpected close.\r\n"); |
AdamGreen | 0:616601bde9fb | 799 | } |
AdamGreen | 0:616601bde9fb | 800 | |
AdamGreen | 0:616601bde9fb | 801 | // Free the HTTP context record and any resources it owned. |
AdamGreen | 0:616601bde9fb | 802 | if (m_WaitingForBuffer) |
AdamGreen | 0:616601bde9fb | 803 | { |
AdamGreen | 0:616601bde9fb | 804 | m_pHTTPServer->RemoveWaitingContext(this); |
AdamGreen | 0:616601bde9fb | 805 | } |
AdamGreen | 0:616601bde9fb | 806 | for (i = 0 ; i < ARRAYSIZE(m_BufferQueue) ; i++) |
AdamGreen | 0:616601bde9fb | 807 | { |
AdamGreen | 0:616601bde9fb | 808 | SBuffer* pBuffer = m_BufferQueue[i]; |
AdamGreen | 0:616601bde9fb | 809 | if (pBuffer) |
AdamGreen | 0:616601bde9fb | 810 | { |
AdamGreen | 0:616601bde9fb | 811 | if (ForcedFree) |
AdamGreen | 0:616601bde9fb | 812 | { |
AdamGreen | 0:616601bde9fb | 813 | // 0 out the counts if we are forcing a free. |
AdamGreen | 0:616601bde9fb | 814 | pBuffer->BytesToWrite = 0; |
AdamGreen | 0:616601bde9fb | 815 | pBuffer->UnacknowledgedBytes = 0; |
AdamGreen | 0:616601bde9fb | 816 | } |
AdamGreen | 0:616601bde9fb | 817 | |
AdamGreen | 0:616601bde9fb | 818 | // Free the buffer. |
AdamGreen | 0:616601bde9fb | 819 | m_pHTTPServer->FreeBuffer(m_BufferQueue[i]); |
AdamGreen | 0:616601bde9fb | 820 | m_BufferQueue[i] = NULL; |
AdamGreen | 0:616601bde9fb | 821 | } |
AdamGreen | 0:616601bde9fb | 822 | } |
AdamGreen | 0:616601bde9fb | 823 | if (m_pRequestHandlerContext) |
AdamGreen | 0:616601bde9fb | 824 | { |
AdamGreen | 0:616601bde9fb | 825 | m_pRequestHandlerContext->Release(); |
AdamGreen | 0:616601bde9fb | 826 | m_pRequestHandlerContext = NULL; |
AdamGreen | 0:616601bde9fb | 827 | } |
AdamGreen | 0:616601bde9fb | 828 | |
AdamGreen | 0:616601bde9fb | 829 | // Clear the callbacks for the PCB as we are getting ready to close it. |
AdamGreen | 0:616601bde9fb | 830 | if (m_pPCB) |
AdamGreen | 0:616601bde9fb | 831 | { |
AdamGreen | 0:616601bde9fb | 832 | // Keep the polling callback enabled incase the previous tcp_close() |
AdamGreen | 0:616601bde9fb | 833 | // attempt failed. |
AdamGreen | 0:616601bde9fb | 834 | tcp_arg(m_pPCB, NULL); |
AdamGreen | 0:616601bde9fb | 835 | tcp_accept(m_pPCB, NULL); |
AdamGreen | 0:616601bde9fb | 836 | tcp_recv(m_pPCB, NULL); |
AdamGreen | 0:616601bde9fb | 837 | tcp_sent(m_pPCB, NULL); |
AdamGreen | 0:616601bde9fb | 838 | tcp_err(m_pPCB, NULL); |
AdamGreen | 0:616601bde9fb | 839 | m_pPCB = NULL; |
AdamGreen | 0:616601bde9fb | 840 | } |
AdamGreen | 0:616601bde9fb | 841 | |
AdamGreen | 0:616601bde9fb | 842 | delete this; |
AdamGreen | 0:616601bde9fb | 843 | } |
AdamGreen | 0:616601bde9fb | 844 | |
AdamGreen | 0:616601bde9fb | 845 | |
AdamGreen | 0:616601bde9fb | 846 | /* Processes the HTTP request data as it comes in from the client. |
AdamGreen | 0:616601bde9fb | 847 | |
AdamGreen | 0:616601bde9fb | 848 | Parameters: |
AdamGreen | 0:616601bde9fb | 849 | pBuf points to the buffer of inbound TCP/IP data from the connection. |
AdamGreen | 0:616601bde9fb | 850 | |
AdamGreen | 0:616601bde9fb | 851 | Returns: |
AdamGreen | 0:616601bde9fb | 852 | Nothing. |
AdamGreen | 0:616601bde9fb | 853 | */ |
AdamGreen | 0:616601bde9fb | 854 | void CHTTPContext::ProcessRequestData(pbuf* pBuf) |
AdamGreen | 0:616601bde9fb | 855 | { |
AdamGreen | 0:616601bde9fb | 856 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 857 | assert ( pBuf ); |
AdamGreen | 0:616601bde9fb | 858 | |
AdamGreen | 0:616601bde9fb | 859 | switch (m_ParseState) |
AdamGreen | 0:616601bde9fb | 860 | { |
AdamGreen | 0:616601bde9fb | 861 | case STATE_FIRST_LINE: |
AdamGreen | 0:616601bde9fb | 862 | case STATE_HEADERS: |
AdamGreen | 0:616601bde9fb | 863 | BufferRequestLines(pBuf); |
AdamGreen | 0:616601bde9fb | 864 | break; |
AdamGreen | 0:616601bde9fb | 865 | case STATE_CONTENT: |
AdamGreen | 0:616601bde9fb | 866 | ProcessRequestContent(pBuf); |
AdamGreen | 0:616601bde9fb | 867 | break; |
AdamGreen | 0:616601bde9fb | 868 | case STATE_DONE: |
AdamGreen | 0:616601bde9fb | 869 | // We should have received everything already so discard data and |
AdamGreen | 0:616601bde9fb | 870 | // return. |
AdamGreen | 0:616601bde9fb | 871 | return; |
AdamGreen | 0:616601bde9fb | 872 | } |
AdamGreen | 0:616601bde9fb | 873 | |
AdamGreen | 0:616601bde9fb | 874 | if (STATE_DONE == m_ParseState) |
AdamGreen | 0:616601bde9fb | 875 | { |
AdamGreen | 0:616601bde9fb | 876 | // Let the application interface know that we are done sending it |
AdamGreen | 0:616601bde9fb | 877 | // the request content. |
AdamGreen | 0:616601bde9fb | 878 | if (m_SendRequestContentToHandler) |
AdamGreen | 0:616601bde9fb | 879 | { |
AdamGreen | 0:616601bde9fb | 880 | m_pRequestHandlerContext->EndRequestContent(); |
AdamGreen | 0:616601bde9fb | 881 | } |
AdamGreen | 0:616601bde9fb | 882 | |
AdamGreen | 0:616601bde9fb | 883 | // And prepare response headers and content to send back to client. |
AdamGreen | 0:616601bde9fb | 884 | PrepareResponse(); |
AdamGreen | 0:616601bde9fb | 885 | |
AdamGreen | 0:616601bde9fb | 886 | // Attempt to allocate a buffer and fill it in with header and data |
AdamGreen | 0:616601bde9fb | 887 | // content. |
AdamGreen | 0:616601bde9fb | 888 | ReadContent(); |
AdamGreen | 0:616601bde9fb | 889 | |
AdamGreen | 0:616601bde9fb | 890 | // Attempt to send first segments of response data. |
AdamGreen | 0:616601bde9fb | 891 | SendResponse(); |
AdamGreen | 0:616601bde9fb | 892 | } |
AdamGreen | 0:616601bde9fb | 893 | } |
AdamGreen | 0:616601bde9fb | 894 | |
AdamGreen | 0:616601bde9fb | 895 | |
AdamGreen | 0:616601bde9fb | 896 | /* Buffers up the HTTP requests a line at a time and then processes it once |
AdamGreen | 0:616601bde9fb | 897 | a complete line has been buffered. |
AdamGreen | 0:616601bde9fb | 898 | |
AdamGreen | 0:616601bde9fb | 899 | Parameters: |
AdamGreen | 0:616601bde9fb | 900 | pBuf points to the buffer of inbound TCP/IP data from the connection. |
AdamGreen | 0:616601bde9fb | 901 | |
AdamGreen | 0:616601bde9fb | 902 | Returns: |
AdamGreen | 0:616601bde9fb | 903 | Nothing. |
AdamGreen | 0:616601bde9fb | 904 | */ |
AdamGreen | 0:616601bde9fb | 905 | void CHTTPContext::BufferRequestLines(pbuf* pBuf) |
AdamGreen | 0:616601bde9fb | 906 | { |
AdamGreen | 0:616601bde9fb | 907 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 908 | assert ( pBuf ); |
AdamGreen | 0:616601bde9fb | 909 | |
AdamGreen | 0:616601bde9fb | 910 | // Loop through all chunks in the pbuf list and parse its content a line |
AdamGreen | 0:616601bde9fb | 911 | // at a time. |
AdamGreen | 0:616601bde9fb | 912 | while (pBuf) |
AdamGreen | 0:616601bde9fb | 913 | { |
AdamGreen | 0:616601bde9fb | 914 | char* pCurr = (char*)pBuf->payload; |
AdamGreen | 0:616601bde9fb | 915 | u16_t BytesLeft = pBuf->len; |
AdamGreen | 0:616601bde9fb | 916 | |
AdamGreen | 0:616601bde9fb | 917 | // Loop through the characters in this chunk. |
AdamGreen | 0:616601bde9fb | 918 | while (BytesLeft) |
AdamGreen | 0:616601bde9fb | 919 | { |
AdamGreen | 0:616601bde9fb | 920 | char CurrChar = *pCurr; |
AdamGreen | 0:616601bde9fb | 921 | |
AdamGreen | 0:616601bde9fb | 922 | if ('\n' == CurrChar) |
AdamGreen | 0:616601bde9fb | 923 | { |
AdamGreen | 0:616601bde9fb | 924 | // Have encountered end of line. |
AdamGreen | 0:616601bde9fb | 925 | m_LineBuffer[m_LineBufferOffset] = '\0'; |
AdamGreen | 0:616601bde9fb | 926 | |
AdamGreen | 0:616601bde9fb | 927 | ParseRequestLine(); |
AdamGreen | 0:616601bde9fb | 928 | |
AdamGreen | 0:616601bde9fb | 929 | // Reset pointer to start buffering next line. |
AdamGreen | 0:616601bde9fb | 930 | m_LineBufferOffset = 0; |
AdamGreen | 0:616601bde9fb | 931 | } |
AdamGreen | 0:616601bde9fb | 932 | else if ('\r' != CurrChar && |
AdamGreen | 0:616601bde9fb | 933 | m_LineBufferOffset < ARRAYSIZE(m_LineBuffer)-1) |
AdamGreen | 0:616601bde9fb | 934 | { |
AdamGreen | 0:616601bde9fb | 935 | // Copy character into line buffer. Above conditional protects |
AdamGreen | 0:616601bde9fb | 936 | // from buffer overflow on the write and leaves room for NULL |
AdamGreen | 0:616601bde9fb | 937 | // terminator. |
AdamGreen | 0:616601bde9fb | 938 | m_LineBuffer[m_LineBufferOffset++] = CurrChar; |
AdamGreen | 0:616601bde9fb | 939 | } |
AdamGreen | 0:616601bde9fb | 940 | |
AdamGreen | 0:616601bde9fb | 941 | // Advance to the next character in this chunk. |
AdamGreen | 0:616601bde9fb | 942 | pCurr++; |
AdamGreen | 0:616601bde9fb | 943 | BytesLeft--; |
AdamGreen | 0:616601bde9fb | 944 | } |
AdamGreen | 0:616601bde9fb | 945 | |
AdamGreen | 0:616601bde9fb | 946 | // Advance to the next chunk in the pbuf list. |
AdamGreen | 0:616601bde9fb | 947 | pBuf = pBuf->next; |
AdamGreen | 0:616601bde9fb | 948 | } |
AdamGreen | 0:616601bde9fb | 949 | } |
AdamGreen | 0:616601bde9fb | 950 | |
AdamGreen | 0:616601bde9fb | 951 | |
AdamGreen | 0:616601bde9fb | 952 | /* Processes the HTTP request content data as it comes in from the client. |
AdamGreen | 0:616601bde9fb | 953 | |
AdamGreen | 0:616601bde9fb | 954 | Parameters: |
AdamGreen | 0:616601bde9fb | 955 | pBuf points to the buffer of inbound TCP/IP data from the connection. |
AdamGreen | 0:616601bde9fb | 956 | |
AdamGreen | 0:616601bde9fb | 957 | Returns: |
AdamGreen | 0:616601bde9fb | 958 | Nothing. |
AdamGreen | 0:616601bde9fb | 959 | */ |
AdamGreen | 0:616601bde9fb | 960 | void CHTTPContext::ProcessRequestContent(pbuf* pBuf) |
AdamGreen | 0:616601bde9fb | 961 | { |
AdamGreen | 0:616601bde9fb | 962 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 963 | assert ( pBuf ); |
AdamGreen | 0:616601bde9fb | 964 | |
AdamGreen | 0:616601bde9fb | 965 | while (pBuf && m_RequestContentLength > 0); |
AdamGreen | 0:616601bde9fb | 966 | { |
AdamGreen | 0:616601bde9fb | 967 | size_t BytesToWrite = pBuf->len; |
AdamGreen | 0:616601bde9fb | 968 | if (BytesToWrite > m_RequestContentLength) |
AdamGreen | 0:616601bde9fb | 969 | { |
AdamGreen | 0:616601bde9fb | 970 | // Make sure that we don't send the application interface more |
AdamGreen | 0:616601bde9fb | 971 | // content than we told it we would send. |
AdamGreen | 0:616601bde9fb | 972 | BytesToWrite = m_RequestContentLength; |
AdamGreen | 0:616601bde9fb | 973 | } |
AdamGreen | 0:616601bde9fb | 974 | if (m_SendRequestContentToHandler) |
AdamGreen | 0:616601bde9fb | 975 | { |
AdamGreen | 0:616601bde9fb | 976 | // Only send the application interface the request content if |
AdamGreen | 0:616601bde9fb | 977 | // it has indicated that it wants to receive the data. |
AdamGreen | 0:616601bde9fb | 978 | m_pRequestHandlerContext->WriteRequestContentBlock(pBuf->payload, BytesToWrite); |
AdamGreen | 0:616601bde9fb | 979 | } |
AdamGreen | 0:616601bde9fb | 980 | m_RequestContentLength -= BytesToWrite; |
AdamGreen | 0:616601bde9fb | 981 | |
AdamGreen | 0:616601bde9fb | 982 | // Advance to next chunk in the pbuf list. |
AdamGreen | 0:616601bde9fb | 983 | pBuf = pBuf->next; |
AdamGreen | 0:616601bde9fb | 984 | } |
AdamGreen | 0:616601bde9fb | 985 | |
AdamGreen | 0:616601bde9fb | 986 | if (0 == m_RequestContentLength) |
AdamGreen | 0:616601bde9fb | 987 | { |
AdamGreen | 0:616601bde9fb | 988 | // Have finished receiving the request content so advance to the next |
AdamGreen | 0:616601bde9fb | 989 | // state where response data is sent to the client. |
AdamGreen | 0:616601bde9fb | 990 | m_ParseState = STATE_DONE; |
AdamGreen | 0:616601bde9fb | 991 | } |
AdamGreen | 0:616601bde9fb | 992 | } |
AdamGreen | 0:616601bde9fb | 993 | |
AdamGreen | 0:616601bde9fb | 994 | |
AdamGreen | 0:616601bde9fb | 995 | /* Attempts to parse the current line in m_LineBuffer. |
AdamGreen | 0:616601bde9fb | 996 | |
AdamGreen | 0:616601bde9fb | 997 | Parameters: |
AdamGreen | 0:616601bde9fb | 998 | None. |
AdamGreen | 0:616601bde9fb | 999 | |
AdamGreen | 0:616601bde9fb | 1000 | Returns: |
AdamGreen | 0:616601bde9fb | 1001 | Nothing. |
AdamGreen | 0:616601bde9fb | 1002 | */ |
AdamGreen | 0:616601bde9fb | 1003 | void CHTTPContext::ParseRequestLine() |
AdamGreen | 0:616601bde9fb | 1004 | { |
AdamGreen | 0:616601bde9fb | 1005 | switch(m_ParseState) |
AdamGreen | 0:616601bde9fb | 1006 | { |
AdamGreen | 0:616601bde9fb | 1007 | case STATE_FIRST_LINE: |
AdamGreen | 0:616601bde9fb | 1008 | ParseFirstRequestLine(); |
AdamGreen | 0:616601bde9fb | 1009 | break; |
AdamGreen | 0:616601bde9fb | 1010 | case STATE_HEADERS: |
AdamGreen | 0:616601bde9fb | 1011 | ParseHeaderRequestLine(); |
AdamGreen | 0:616601bde9fb | 1012 | break; |
AdamGreen | 0:616601bde9fb | 1013 | default: |
AdamGreen | 0:616601bde9fb | 1014 | assert ( FALSE ); |
AdamGreen | 0:616601bde9fb | 1015 | break; |
AdamGreen | 0:616601bde9fb | 1016 | } |
AdamGreen | 0:616601bde9fb | 1017 | } |
AdamGreen | 0:616601bde9fb | 1018 | |
AdamGreen | 0:616601bde9fb | 1019 | |
AdamGreen | 0:616601bde9fb | 1020 | /* Attempts to parse the first request line found in m_LineBuffer which should |
AdamGreen | 0:616601bde9fb | 1021 | contain the request method, URI, etc. |
AdamGreen | 0:616601bde9fb | 1022 | |
AdamGreen | 0:616601bde9fb | 1023 | Parameters: |
AdamGreen | 0:616601bde9fb | 1024 | None. |
AdamGreen | 0:616601bde9fb | 1025 | |
AdamGreen | 0:616601bde9fb | 1026 | Returns: |
AdamGreen | 0:616601bde9fb | 1027 | Nothing. |
AdamGreen | 0:616601bde9fb | 1028 | */ |
AdamGreen | 0:616601bde9fb | 1029 | void CHTTPContext::ParseFirstRequestLine() |
AdamGreen | 0:616601bde9fb | 1030 | { |
AdamGreen | 0:616601bde9fb | 1031 | static const char GetMethod[] = "GET "; |
AdamGreen | 0:616601bde9fb | 1032 | static const char HeadMethod[] = "HEAD "; |
AdamGreen | 0:616601bde9fb | 1033 | static const char PostMethod[] = "POST "; |
AdamGreen | 0:616601bde9fb | 1034 | const char* pURIStart = NULL; |
AdamGreen | 0:616601bde9fb | 1035 | const char* pURISrc = NULL; |
AdamGreen | 0:616601bde9fb | 1036 | char* pURIDest = NULL; |
AdamGreen | 0:616601bde9fb | 1037 | char UnescapedChar = 0; |
AdamGreen | 0:616601bde9fb | 1038 | int CharsToEscape = 0; |
AdamGreen | 0:616601bde9fb | 1039 | |
AdamGreen | 0:616601bde9fb | 1040 | // Advance to next state. |
AdamGreen | 0:616601bde9fb | 1041 | m_ParseState = STATE_HEADERS; |
AdamGreen | 0:616601bde9fb | 1042 | |
AdamGreen | 0:616601bde9fb | 1043 | // This is the first line which indicates method being requested |
AdamGreen | 0:616601bde9fb | 1044 | // (ie. GET, HEAD, POST). |
AdamGreen | 0:616601bde9fb | 1045 | if (0 == strncmp(m_LineBuffer, GetMethod, sizeof(GetMethod)-1)) |
AdamGreen | 0:616601bde9fb | 1046 | { |
AdamGreen | 0:616601bde9fb | 1047 | // Assume a HTTP/1.0 GET request for now. Will know later if we |
AdamGreen | 0:616601bde9fb | 1048 | // need to downgrade it to a 0.9 request when version isn't sent. |
AdamGreen | 0:616601bde9fb | 1049 | m_RequestType = TYPE_GET_1_0; |
AdamGreen | 0:616601bde9fb | 1050 | pURISrc = &m_LineBuffer[sizeof(GetMethod)-1]; |
AdamGreen | 0:616601bde9fb | 1051 | } |
AdamGreen | 0:616601bde9fb | 1052 | else if (0 == strncmp(m_LineBuffer, HeadMethod, sizeof(HeadMethod)-1)) |
AdamGreen | 0:616601bde9fb | 1053 | { |
AdamGreen | 0:616601bde9fb | 1054 | m_RequestType = TYPE_HEAD; |
AdamGreen | 0:616601bde9fb | 1055 | pURISrc = &m_LineBuffer[sizeof(HeadMethod)-1]; |
AdamGreen | 0:616601bde9fb | 1056 | } |
AdamGreen | 0:616601bde9fb | 1057 | else if (0 == strncmp(m_LineBuffer, PostMethod, sizeof(PostMethod)-1)) |
AdamGreen | 0:616601bde9fb | 1058 | { |
AdamGreen | 0:616601bde9fb | 1059 | m_RequestType = TYPE_POST; |
AdamGreen | 0:616601bde9fb | 1060 | pURISrc = &m_LineBuffer[sizeof(PostMethod)-1]; |
AdamGreen | 0:616601bde9fb | 1061 | } |
AdamGreen | 0:616601bde9fb | 1062 | else |
AdamGreen | 0:616601bde9fb | 1063 | { |
AdamGreen | 0:616601bde9fb | 1064 | // Don't recognize this method so mark it as bad. |
AdamGreen | 0:616601bde9fb | 1065 | m_RequestType = TYPE_BAD_REQUEST; |
AdamGreen | 0:616601bde9fb | 1066 | |
AdamGreen | 0:616601bde9fb | 1067 | // See if the application recognizes and wants to handle it by sending |
AdamGreen | 0:616601bde9fb | 1068 | // it the full request line. |
AdamGreen | 0:616601bde9fb | 1069 | StartBadRequest(m_LineBuffer); |
AdamGreen | 0:616601bde9fb | 1070 | return; |
AdamGreen | 0:616601bde9fb | 1071 | } |
AdamGreen | 0:616601bde9fb | 1072 | |
AdamGreen | 0:616601bde9fb | 1073 | // Skip additional whitespace that might precede URI even though there |
AdamGreen | 0:616601bde9fb | 1074 | // shouldn't be any just to be lenient. |
AdamGreen | 0:616601bde9fb | 1075 | while (' ' == *pURISrc) |
AdamGreen | 0:616601bde9fb | 1076 | { |
AdamGreen | 0:616601bde9fb | 1077 | pURISrc++; |
AdamGreen | 0:616601bde9fb | 1078 | } |
AdamGreen | 0:616601bde9fb | 1079 | pURIStart = pURISrc; |
AdamGreen | 0:616601bde9fb | 1080 | pURIDest = (char*)(void*)pURISrc; |
AdamGreen | 0:616601bde9fb | 1081 | |
AdamGreen | 0:616601bde9fb | 1082 | // Save away the URI character by character until whitespace or end |
AdamGreen | 0:616601bde9fb | 1083 | // of line is encountered. |
AdamGreen | 0:616601bde9fb | 1084 | while(' ' != *pURISrc && |
AdamGreen | 0:616601bde9fb | 1085 | '\0' != *pURISrc) |
AdamGreen | 0:616601bde9fb | 1086 | { |
AdamGreen | 0:616601bde9fb | 1087 | char CurrChar = *pURISrc; |
AdamGreen | 0:616601bde9fb | 1088 | |
AdamGreen | 0:616601bde9fb | 1089 | // Unescape % HEX HEX portions of the URI |
AdamGreen | 0:616601bde9fb | 1090 | if ('%' == CurrChar) |
AdamGreen | 0:616601bde9fb | 1091 | { |
AdamGreen | 0:616601bde9fb | 1092 | CharsToEscape = 2; |
AdamGreen | 0:616601bde9fb | 1093 | UnescapedChar = 0; |
AdamGreen | 0:616601bde9fb | 1094 | } |
AdamGreen | 0:616601bde9fb | 1095 | else if (CharsToEscape) |
AdamGreen | 0:616601bde9fb | 1096 | { |
AdamGreen | 0:616601bde9fb | 1097 | UnescapedChar <<= 4; |
AdamGreen | 0:616601bde9fb | 1098 | |
AdamGreen | 0:616601bde9fb | 1099 | if (CurrChar >= 'a' && CurrChar <= 'f') |
AdamGreen | 0:616601bde9fb | 1100 | { |
AdamGreen | 0:616601bde9fb | 1101 | CurrChar = (CurrChar - 'a') + 10; |
AdamGreen | 0:616601bde9fb | 1102 | } |
AdamGreen | 0:616601bde9fb | 1103 | else if (CurrChar >= 'A' && CurrChar <= 'F') |
AdamGreen | 0:616601bde9fb | 1104 | { |
AdamGreen | 0:616601bde9fb | 1105 | CurrChar = (CurrChar - 'A') + 10; |
AdamGreen | 0:616601bde9fb | 1106 | } |
AdamGreen | 0:616601bde9fb | 1107 | else if (CurrChar >= '0' && CurrChar <= '9') |
AdamGreen | 0:616601bde9fb | 1108 | { |
AdamGreen | 0:616601bde9fb | 1109 | CurrChar = CurrChar - '0'; |
AdamGreen | 0:616601bde9fb | 1110 | } |
AdamGreen | 0:616601bde9fb | 1111 | else |
AdamGreen | 0:616601bde9fb | 1112 | { |
AdamGreen | 0:616601bde9fb | 1113 | // Not a hex digit. |
AdamGreen | 0:616601bde9fb | 1114 | CurrChar = 0; |
AdamGreen | 0:616601bde9fb | 1115 | } |
AdamGreen | 0:616601bde9fb | 1116 | |
AdamGreen | 0:616601bde9fb | 1117 | UnescapedChar |= (CurrChar & 0xF); |
AdamGreen | 0:616601bde9fb | 1118 | |
AdamGreen | 0:616601bde9fb | 1119 | if (0 == --CharsToEscape) |
AdamGreen | 0:616601bde9fb | 1120 | { |
AdamGreen | 0:616601bde9fb | 1121 | *pURIDest++ = UnescapedChar; |
AdamGreen | 0:616601bde9fb | 1122 | } |
AdamGreen | 0:616601bde9fb | 1123 | } |
AdamGreen | 0:616601bde9fb | 1124 | else |
AdamGreen | 0:616601bde9fb | 1125 | { |
AdamGreen | 0:616601bde9fb | 1126 | *pURIDest++ = CurrChar; |
AdamGreen | 0:616601bde9fb | 1127 | } |
AdamGreen | 0:616601bde9fb | 1128 | pURISrc++; |
AdamGreen | 0:616601bde9fb | 1129 | } |
AdamGreen | 0:616601bde9fb | 1130 | |
AdamGreen | 0:616601bde9fb | 1131 | if (' ' != *pURISrc) |
AdamGreen | 0:616601bde9fb | 1132 | { |
AdamGreen | 0:616601bde9fb | 1133 | // Protocol string didn't follow URI so must be a HTTP/0.9 request. |
AdamGreen | 0:616601bde9fb | 1134 | if (TYPE_GET_1_0 == m_RequestType) |
AdamGreen | 0:616601bde9fb | 1135 | { |
AdamGreen | 0:616601bde9fb | 1136 | m_RequestType = TYPE_GET_0_9; |
AdamGreen | 0:616601bde9fb | 1137 | // This will have been the last request line for a v0.9 request so |
AdamGreen | 0:616601bde9fb | 1138 | // advance to the finished state. |
AdamGreen | 0:616601bde9fb | 1139 | m_ParseState = STATE_DONE; |
AdamGreen | 0:616601bde9fb | 1140 | } |
AdamGreen | 0:616601bde9fb | 1141 | else |
AdamGreen | 0:616601bde9fb | 1142 | { |
AdamGreen | 0:616601bde9fb | 1143 | m_RequestType = TYPE_BAD_REQUEST; |
AdamGreen | 0:616601bde9fb | 1144 | |
AdamGreen | 0:616601bde9fb | 1145 | // See if the application recognizes and wants to handle it by sending |
AdamGreen | 0:616601bde9fb | 1146 | // it the full request line. |
AdamGreen | 0:616601bde9fb | 1147 | StartBadRequest(m_LineBuffer); |
AdamGreen | 0:616601bde9fb | 1148 | return; |
AdamGreen | 0:616601bde9fb | 1149 | } |
AdamGreen | 0:616601bde9fb | 1150 | } |
AdamGreen | 0:616601bde9fb | 1151 | *pURIDest = '\0'; |
AdamGreen | 0:616601bde9fb | 1152 | |
AdamGreen | 0:616601bde9fb | 1153 | StartRequestHandler(pURIStart); |
AdamGreen | 0:616601bde9fb | 1154 | } |
AdamGreen | 0:616601bde9fb | 1155 | |
AdamGreen | 0:616601bde9fb | 1156 | |
AdamGreen | 0:616601bde9fb | 1157 | /* Attempts to parse the header request lines until a blank request line is |
AdamGreen | 0:616601bde9fb | 1158 | encountered which indicates that start of the content. |
AdamGreen | 0:616601bde9fb | 1159 | |
AdamGreen | 0:616601bde9fb | 1160 | Parameters: |
AdamGreen | 0:616601bde9fb | 1161 | None. |
AdamGreen | 0:616601bde9fb | 1162 | |
AdamGreen | 0:616601bde9fb | 1163 | Returns: |
AdamGreen | 0:616601bde9fb | 1164 | Nothing. |
AdamGreen | 0:616601bde9fb | 1165 | */ |
AdamGreen | 0:616601bde9fb | 1166 | void CHTTPContext::ParseHeaderRequestLine() |
AdamGreen | 0:616601bde9fb | 1167 | { |
AdamGreen | 0:616601bde9fb | 1168 | static const char ContentLengthName[] = "Content-Length"; |
AdamGreen | 0:616601bde9fb | 1169 | char* pName = m_LineBuffer; |
AdamGreen | 0:616601bde9fb | 1170 | char* pValue = NULL; |
AdamGreen | 0:616601bde9fb | 1171 | char* pCurr = m_LineBuffer; |
AdamGreen | 0:616601bde9fb | 1172 | |
AdamGreen | 0:616601bde9fb | 1173 | if ('\0' == m_LineBuffer[0]) |
AdamGreen | 0:616601bde9fb | 1174 | { |
AdamGreen | 0:616601bde9fb | 1175 | // Let the request handler know that we have reached the last header. |
AdamGreen | 0:616601bde9fb | 1176 | if (m_SendRequestHeadersToHandler) |
AdamGreen | 0:616601bde9fb | 1177 | { |
AdamGreen | 0:616601bde9fb | 1178 | m_pRequestHandlerContext->EndRequestHeaders(); |
AdamGreen | 0:616601bde9fb | 1179 | } |
AdamGreen | 0:616601bde9fb | 1180 | |
AdamGreen | 0:616601bde9fb | 1181 | if (m_RequestContentLength) |
AdamGreen | 0:616601bde9fb | 1182 | { |
AdamGreen | 0:616601bde9fb | 1183 | // Determine if the request handler is interested in any request |
AdamGreen | 0:616601bde9fb | 1184 | // content data that might follow the headers. |
AdamGreen | 0:616601bde9fb | 1185 | m_ParseState = STATE_CONTENT; |
AdamGreen | 0:616601bde9fb | 1186 | m_SendRequestContentToHandler = m_pRequestHandlerContext->BeginRequestContent(m_RequestContentLength); |
AdamGreen | 0:616601bde9fb | 1187 | } |
AdamGreen | 0:616601bde9fb | 1188 | else |
AdamGreen | 0:616601bde9fb | 1189 | { |
AdamGreen | 0:616601bde9fb | 1190 | // There is no request content from the client so server can start |
AdamGreen | 0:616601bde9fb | 1191 | // to send its response now. |
AdamGreen | 0:616601bde9fb | 1192 | m_ParseState = STATE_DONE; |
AdamGreen | 0:616601bde9fb | 1193 | } |
AdamGreen | 0:616601bde9fb | 1194 | |
AdamGreen | 0:616601bde9fb | 1195 | return; |
AdamGreen | 0:616601bde9fb | 1196 | } |
AdamGreen | 0:616601bde9fb | 1197 | |
AdamGreen | 0:616601bde9fb | 1198 | // Find the colon character which terminates the field name. A whitespace |
AdamGreen | 0:616601bde9fb | 1199 | // character at the beginning of a header line indicates a line |
AdamGreen | 0:616601bde9fb | 1200 | // continuation so handle that as well. |
AdamGreen | 0:616601bde9fb | 1201 | while (':' != *pCurr && |
AdamGreen | 0:616601bde9fb | 1202 | ' ' != *pCurr && |
AdamGreen | 0:616601bde9fb | 1203 | '\t' != *pCurr ) |
AdamGreen | 0:616601bde9fb | 1204 | { |
AdamGreen | 0:616601bde9fb | 1205 | pCurr++; |
AdamGreen | 0:616601bde9fb | 1206 | } |
AdamGreen | 0:616601bde9fb | 1207 | *pCurr++ = '\0'; |
AdamGreen | 0:616601bde9fb | 1208 | |
AdamGreen | 0:616601bde9fb | 1209 | // Skip over whitespace to find start of value. |
AdamGreen | 0:616601bde9fb | 1210 | while (' ' == *pCurr || |
AdamGreen | 0:616601bde9fb | 1211 | '\t' == *pCurr) |
AdamGreen | 0:616601bde9fb | 1212 | { |
AdamGreen | 0:616601bde9fb | 1213 | pCurr++; |
AdamGreen | 0:616601bde9fb | 1214 | } |
AdamGreen | 0:616601bde9fb | 1215 | pValue = pCurr; |
AdamGreen | 0:616601bde9fb | 1216 | |
AdamGreen | 0:616601bde9fb | 1217 | // Check for the Content-Length field which tells the server if it should |
AdamGreen | 0:616601bde9fb | 1218 | // expect the request to have a content body. |
AdamGreen | 0:616601bde9fb | 1219 | if (0 == strcasecmp(pName, ContentLengthName)) |
AdamGreen | 0:616601bde9fb | 1220 | { |
AdamGreen | 0:616601bde9fb | 1221 | m_RequestContentLength = strtoul(pValue, NULL, 10); |
AdamGreen | 0:616601bde9fb | 1222 | } |
AdamGreen | 0:616601bde9fb | 1223 | |
AdamGreen | 0:616601bde9fb | 1224 | // Send the header to request handler context interface if it has requested |
AdamGreen | 0:616601bde9fb | 1225 | // the server to do so. |
AdamGreen | 0:616601bde9fb | 1226 | if (m_SendRequestHeadersToHandler) |
AdamGreen | 0:616601bde9fb | 1227 | { |
AdamGreen | 0:616601bde9fb | 1228 | m_pRequestHandlerContext->WriteRequestHeader(pName, pValue); |
AdamGreen | 0:616601bde9fb | 1229 | } |
AdamGreen | 0:616601bde9fb | 1230 | } |
AdamGreen | 0:616601bde9fb | 1231 | |
AdamGreen | 0:616601bde9fb | 1232 | |
AdamGreen | 0:616601bde9fb | 1233 | /* Kicks off the process of handling client specified request. |
AdamGreen | 0:616601bde9fb | 1234 | |
AdamGreen | 0:616601bde9fb | 1235 | Parameters: |
AdamGreen | 0:616601bde9fb | 1236 | pURI is the name of the URI being requested. |
AdamGreen | 0:616601bde9fb | 1237 | |
AdamGreen | 0:616601bde9fb | 1238 | Returns: |
AdamGreen | 0:616601bde9fb | 1239 | Nothing. |
AdamGreen | 0:616601bde9fb | 1240 | */ |
AdamGreen | 0:616601bde9fb | 1241 | void CHTTPContext::StartRequestHandler(const char* pURI) |
AdamGreen | 0:616601bde9fb | 1242 | { |
AdamGreen | 0:616601bde9fb | 1243 | // Start request by first letting application provider a handler and if it |
AdamGreen | 0:616601bde9fb | 1244 | // doesn't use this object's default request handler interface instead. |
AdamGreen | 0:616601bde9fb | 1245 | switch (m_RequestType) |
AdamGreen | 0:616601bde9fb | 1246 | { |
AdamGreen | 0:616601bde9fb | 1247 | case TYPE_GET_0_9: |
AdamGreen | 0:616601bde9fb | 1248 | case TYPE_GET_1_0: |
AdamGreen | 0:616601bde9fb | 1249 | StartGetRequest(pURI); |
AdamGreen | 0:616601bde9fb | 1250 | break; |
AdamGreen | 0:616601bde9fb | 1251 | case TYPE_HEAD: |
AdamGreen | 0:616601bde9fb | 1252 | StartHeadRequest(pURI); |
AdamGreen | 0:616601bde9fb | 1253 | break; |
AdamGreen | 0:616601bde9fb | 1254 | case TYPE_POST: |
AdamGreen | 0:616601bde9fb | 1255 | StartPostRequest(pURI); |
AdamGreen | 0:616601bde9fb | 1256 | break; |
AdamGreen | 0:616601bde9fb | 1257 | default: |
AdamGreen | 0:616601bde9fb | 1258 | assert ( FALSE ); |
AdamGreen | 0:616601bde9fb | 1259 | break; |
AdamGreen | 0:616601bde9fb | 1260 | } |
AdamGreen | 0:616601bde9fb | 1261 | |
AdamGreen | 0:616601bde9fb | 1262 | // Should have a request handler now: either from application or this |
AdamGreen | 0:616601bde9fb | 1263 | // context object. |
AdamGreen | 0:616601bde9fb | 1264 | assert ( m_pRequestHandlerContext ); |
AdamGreen | 0:616601bde9fb | 1265 | |
AdamGreen | 0:616601bde9fb | 1266 | // Ask the request handler interface if it wants each request header sent |
AdamGreen | 0:616601bde9fb | 1267 | // to it. There will be no header for HTTP/0.9 GET request which skips the |
AdamGreen | 0:616601bde9fb | 1268 | // header state. |
AdamGreen | 0:616601bde9fb | 1269 | if (STATE_HEADERS == m_ParseState) |
AdamGreen | 0:616601bde9fb | 1270 | { |
AdamGreen | 0:616601bde9fb | 1271 | m_SendRequestHeadersToHandler = m_pRequestHandlerContext->BeginRequestHeaders(); |
AdamGreen | 0:616601bde9fb | 1272 | } |
AdamGreen | 0:616601bde9fb | 1273 | } |
AdamGreen | 0:616601bde9fb | 1274 | |
AdamGreen | 0:616601bde9fb | 1275 | |
AdamGreen | 0:616601bde9fb | 1276 | /* Handles GET requests by first allowing the application to handler it via a |
AdamGreen | 0:616601bde9fb | 1277 | register request handler. If it doesn't want to handle the request then the |
AdamGreen | 0:616601bde9fb | 1278 | server will attempt to satisfy the request from the file system that was |
AdamGreen | 0:616601bde9fb | 1279 | registers at bind time. |
AdamGreen | 0:616601bde9fb | 1280 | |
AdamGreen | 0:616601bde9fb | 1281 | Parameters: |
AdamGreen | 0:616601bde9fb | 1282 | pURI is the name of the URI being requested. |
AdamGreen | 0:616601bde9fb | 1283 | |
AdamGreen | 0:616601bde9fb | 1284 | Returns: |
AdamGreen | 0:616601bde9fb | 1285 | Nothing. |
AdamGreen | 0:616601bde9fb | 1286 | */ |
AdamGreen | 0:616601bde9fb | 1287 | void CHTTPContext::StartGetRequest(const char* pURI) |
AdamGreen | 0:616601bde9fb | 1288 | { |
AdamGreen | 0:616601bde9fb | 1289 | // Load the requested file if possible or the file not found |
AdamGreen | 0:616601bde9fb | 1290 | // content if requested file was not found. |
AdamGreen | 0:616601bde9fb | 1291 | TRACE("HTTP: GET '%s' from: ", pURI); |
AdamGreen | 0:616601bde9fb | 1292 | NetworkPrintRemoteAddress(m_pPCB); |
AdamGreen | 0:616601bde9fb | 1293 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 1294 | |
AdamGreen | 0:616601bde9fb | 1295 | if (m_pHTTPServer->m_pRequestHandler) |
AdamGreen | 0:616601bde9fb | 1296 | { |
AdamGreen | 0:616601bde9fb | 1297 | // First let the application attempt to handle the GET request. |
AdamGreen | 0:616601bde9fb | 1298 | m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandleGetRequest(pURI); |
AdamGreen | 0:616601bde9fb | 1299 | } |
AdamGreen | 0:616601bde9fb | 1300 | if (!m_pRequestHandlerContext) |
AdamGreen | 0:616601bde9fb | 1301 | { |
AdamGreen | 0:616601bde9fb | 1302 | // The application doesn't want to handle the GET request so use the |
AdamGreen | 0:616601bde9fb | 1303 | // default server behaviour. |
AdamGreen | 0:616601bde9fb | 1304 | OpenFile(pURI); |
AdamGreen | 0:616601bde9fb | 1305 | m_pRequestHandlerContext = this; |
AdamGreen | 0:616601bde9fb | 1306 | } |
AdamGreen | 0:616601bde9fb | 1307 | } |
AdamGreen | 0:616601bde9fb | 1308 | |
AdamGreen | 0:616601bde9fb | 1309 | |
AdamGreen | 0:616601bde9fb | 1310 | /* Handles HEAD requests by first allowing the application to handler it via a |
AdamGreen | 0:616601bde9fb | 1311 | register request handler. If it doesn't want to handle the request then the |
AdamGreen | 0:616601bde9fb | 1312 | server will attempt to satisfy the request from the file system that was |
AdamGreen | 0:616601bde9fb | 1313 | registers at bind time. |
AdamGreen | 0:616601bde9fb | 1314 | |
AdamGreen | 0:616601bde9fb | 1315 | Parameters: |
AdamGreen | 0:616601bde9fb | 1316 | pURI is the name of the URI being requested. |
AdamGreen | 0:616601bde9fb | 1317 | |
AdamGreen | 0:616601bde9fb | 1318 | Returns: |
AdamGreen | 0:616601bde9fb | 1319 | Nothing. |
AdamGreen | 0:616601bde9fb | 1320 | */ |
AdamGreen | 0:616601bde9fb | 1321 | void CHTTPContext::StartHeadRequest(const char* pURI) |
AdamGreen | 0:616601bde9fb | 1322 | { |
AdamGreen | 0:616601bde9fb | 1323 | // Load the requested file if possible or the file not found |
AdamGreen | 0:616601bde9fb | 1324 | // content if requested file was not found. |
AdamGreen | 0:616601bde9fb | 1325 | TRACE("HTTP: HEAD '%s' from: ", pURI); |
AdamGreen | 0:616601bde9fb | 1326 | NetworkPrintRemoteAddress(m_pPCB); |
AdamGreen | 0:616601bde9fb | 1327 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 1328 | |
AdamGreen | 0:616601bde9fb | 1329 | if (m_pHTTPServer->m_pRequestHandler) |
AdamGreen | 0:616601bde9fb | 1330 | { |
AdamGreen | 0:616601bde9fb | 1331 | // First let the application attempt to handle the HEAD request. |
AdamGreen | 0:616601bde9fb | 1332 | m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandleHeadRequest(pURI); |
AdamGreen | 0:616601bde9fb | 1333 | } |
AdamGreen | 0:616601bde9fb | 1334 | |
AdamGreen | 0:616601bde9fb | 1335 | if (!m_pRequestHandlerContext) |
AdamGreen | 0:616601bde9fb | 1336 | { |
AdamGreen | 0:616601bde9fb | 1337 | // The application doesn't want to handle the HEAD request so use the |
AdamGreen | 0:616601bde9fb | 1338 | // default server behaviour. |
AdamGreen | 0:616601bde9fb | 1339 | OpenFile(pURI); |
AdamGreen | 0:616601bde9fb | 1340 | m_pRequestHandlerContext = this; |
AdamGreen | 0:616601bde9fb | 1341 | } |
AdamGreen | 0:616601bde9fb | 1342 | } |
AdamGreen | 0:616601bde9fb | 1343 | |
AdamGreen | 0:616601bde9fb | 1344 | |
AdamGreen | 0:616601bde9fb | 1345 | /* Handles POST requests by allowing the application to handler it via a |
AdamGreen | 0:616601bde9fb | 1346 | registered request handler. If it doesn't want to handle the request then |
AdamGreen | 0:616601bde9fb | 1347 | the server will treat it as an unimplemented request. |
AdamGreen | 0:616601bde9fb | 1348 | |
AdamGreen | 0:616601bde9fb | 1349 | Parameters: |
AdamGreen | 0:616601bde9fb | 1350 | pURI is the name of the URI being requested. |
AdamGreen | 0:616601bde9fb | 1351 | |
AdamGreen | 0:616601bde9fb | 1352 | Returns: |
AdamGreen | 0:616601bde9fb | 1353 | Nothing. |
AdamGreen | 0:616601bde9fb | 1354 | */ |
AdamGreen | 0:616601bde9fb | 1355 | void CHTTPContext::StartPostRequest(const char* pURI) |
AdamGreen | 0:616601bde9fb | 1356 | { |
AdamGreen | 0:616601bde9fb | 1357 | // Load the requested file if possible or the file not found |
AdamGreen | 0:616601bde9fb | 1358 | // content if requested file was not found. |
AdamGreen | 0:616601bde9fb | 1359 | TRACE("HTTP: POST '%s' from: ", pURI); |
AdamGreen | 0:616601bde9fb | 1360 | NetworkPrintRemoteAddress(m_pPCB); |
AdamGreen | 0:616601bde9fb | 1361 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 1362 | |
AdamGreen | 0:616601bde9fb | 1363 | if (m_pHTTPServer->m_pRequestHandler) |
AdamGreen | 0:616601bde9fb | 1364 | { |
AdamGreen | 0:616601bde9fb | 1365 | // First let the application attempt to handle the POST request. |
AdamGreen | 0:616601bde9fb | 1366 | m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandlePostRequest(pURI); |
AdamGreen | 0:616601bde9fb | 1367 | } |
AdamGreen | 0:616601bde9fb | 1368 | |
AdamGreen | 0:616601bde9fb | 1369 | if (!m_pRequestHandlerContext) |
AdamGreen | 0:616601bde9fb | 1370 | { |
AdamGreen | 0:616601bde9fb | 1371 | // The application doesn't want to handle the POST request. |
AdamGreen | 0:616601bde9fb | 1372 | m_pRequestHandlerContext = this; |
AdamGreen | 0:616601bde9fb | 1373 | } |
AdamGreen | 0:616601bde9fb | 1374 | } |
AdamGreen | 0:616601bde9fb | 1375 | |
AdamGreen | 0:616601bde9fb | 1376 | |
AdamGreen | 0:616601bde9fb | 1377 | /* Handles unknown/bad HTTP client requests by sending back an appropriate |
AdamGreen | 0:616601bde9fb | 1378 | error response. |
AdamGreen | 0:616601bde9fb | 1379 | |
AdamGreen | 0:616601bde9fb | 1380 | Parameters: |
AdamGreen | 0:616601bde9fb | 1381 | pRequest is the complete request line which wasn't recognized by the server. |
AdamGreen | 0:616601bde9fb | 1382 | |
AdamGreen | 0:616601bde9fb | 1383 | Returns: |
AdamGreen | 0:616601bde9fb | 1384 | Nothing. |
AdamGreen | 0:616601bde9fb | 1385 | */ |
AdamGreen | 0:616601bde9fb | 1386 | void CHTTPContext::StartBadRequest(const char* pRequest) |
AdamGreen | 0:616601bde9fb | 1387 | { |
AdamGreen | 0:616601bde9fb | 1388 | if (m_pHTTPServer->m_pRequestHandler) |
AdamGreen | 0:616601bde9fb | 1389 | { |
AdamGreen | 0:616601bde9fb | 1390 | // First let the application attempt to handle this request instead. |
AdamGreen | 0:616601bde9fb | 1391 | m_pRequestHandlerContext = m_pHTTPServer->m_pRequestHandler->HandleBadRequest(pRequest); |
AdamGreen | 0:616601bde9fb | 1392 | } |
AdamGreen | 0:616601bde9fb | 1393 | if (!m_pRequestHandlerContext) |
AdamGreen | 0:616601bde9fb | 1394 | { |
AdamGreen | 0:616601bde9fb | 1395 | // The application doesn't want to handle the bad request so just |
AdamGreen | 0:616601bde9fb | 1396 | // return an appropriate bad request response. |
AdamGreen | 0:616601bde9fb | 1397 | m_pRequestHandlerContext = this; |
AdamGreen | 0:616601bde9fb | 1398 | } |
AdamGreen | 0:616601bde9fb | 1399 | |
AdamGreen | 0:616601bde9fb | 1400 | // Ask the request handler interface if it wants each request header sent |
AdamGreen | 0:616601bde9fb | 1401 | // to it. |
AdamGreen | 0:616601bde9fb | 1402 | m_SendRequestHeadersToHandler = m_pRequestHandlerContext->BeginRequestHeaders(); |
AdamGreen | 0:616601bde9fb | 1403 | } |
AdamGreen | 0:616601bde9fb | 1404 | |
AdamGreen | 0:616601bde9fb | 1405 | |
AdamGreen | 0:616601bde9fb | 1406 | /* Called from HTTPRecv() to open the specified file that has been requested by |
AdamGreen | 0:616601bde9fb | 1407 | the HTTP client. In addition to opening the specified file, it will also |
AdamGreen | 0:616601bde9fb | 1408 | fill in the headers to match the content being sent vack. It also handles |
AdamGreen | 0:616601bde9fb | 1409 | sending back the correct header if the requested file isn't found. |
AdamGreen | 0:616601bde9fb | 1410 | |
AdamGreen | 0:616601bde9fb | 1411 | Parameters: |
AdamGreen | 0:616601bde9fb | 1412 | pURI is the name of the URI being requested. |
AdamGreen | 0:616601bde9fb | 1413 | |
AdamGreen | 0:616601bde9fb | 1414 | Returns: |
AdamGreen | 0:616601bde9fb | 1415 | Nothing. |
AdamGreen | 0:616601bde9fb | 1416 | */ |
AdamGreen | 0:616601bde9fb | 1417 | void CHTTPContext::OpenFile(const char* pURI) |
AdamGreen | 0:616601bde9fb | 1418 | { |
AdamGreen | 0:616601bde9fb | 1419 | static const char NotFoundHTML[] = "<html>" |
AdamGreen | 0:616601bde9fb | 1420 | "<body><h2>404: File not found.</h2></body>" |
AdamGreen | 0:616601bde9fb | 1421 | "</html>\r\n"; |
AdamGreen | 0:616601bde9fb | 1422 | // The content types supported by this HTTP server. |
AdamGreen | 0:616601bde9fb | 1423 | static const char ContentHTML[] = "Content-type: text/html\r\n"; |
AdamGreen | 0:616601bde9fb | 1424 | static const char ContentGIF[] = "Content-type: image/gif\r\n"; |
AdamGreen | 0:616601bde9fb | 1425 | static const char ContentPNG[] = "Content-type: image/png\r\n"; |
AdamGreen | 0:616601bde9fb | 1426 | static const char ContentJPG[] = "Content-type: image/jpeg\r\n"; |
AdamGreen | 0:616601bde9fb | 1427 | static const char ContentBMP[] = "Content-type: image/bmp\r\n"; |
AdamGreen | 0:616601bde9fb | 1428 | static const char ContentICO[] = "Content-type: image/x-icon\r\n"; |
AdamGreen | 0:616601bde9fb | 1429 | static const char ContentStream[] = "Content-type: application/octet-stream\r\n"; |
AdamGreen | 0:616601bde9fb | 1430 | static const char ContentJScript[] = "Content-type: application/x-javascript\r\n"; |
AdamGreen | 0:616601bde9fb | 1431 | static const char ContentCSS[] = "Content-type: text/css\r\n"; |
AdamGreen | 0:616601bde9fb | 1432 | static const char ContentFlash[] = "Content-type: application/x-shockwave-flash\r\n"; |
AdamGreen | 0:616601bde9fb | 1433 | static const char ContentXML[] = "Content-type: text/xml\r\n"; |
AdamGreen | 0:616601bde9fb | 1434 | static const char ContentDefault[] = "Content-type: text/plain\r\n"; |
AdamGreen | 0:616601bde9fb | 1435 | |
AdamGreen | 0:616601bde9fb | 1436 | // Table used to map file extensions to above content type headers. |
AdamGreen | 0:616601bde9fb | 1437 | #define CONTENT_HEADER(X) X, sizeof(X)-1 |
AdamGreen | 0:616601bde9fb | 1438 | static const struct |
AdamGreen | 0:616601bde9fb | 1439 | { |
AdamGreen | 0:616601bde9fb | 1440 | // File extension. |
AdamGreen | 0:616601bde9fb | 1441 | const char* pExtension; |
AdamGreen | 0:616601bde9fb | 1442 | // Content header to be used for this file extension type. |
AdamGreen | 0:616601bde9fb | 1443 | const char* pContentHeader; |
AdamGreen | 0:616601bde9fb | 1444 | // Length of pContentHeader string without NULL terminator. |
AdamGreen | 0:616601bde9fb | 1445 | unsigned int ContentHeaderLength; |
AdamGreen | 0:616601bde9fb | 1446 | } ContentMapping[] = |
AdamGreen | 0:616601bde9fb | 1447 | { |
AdamGreen | 0:616601bde9fb | 1448 | {".html", CONTENT_HEADER(ContentHTML)}, |
AdamGreen | 0:616601bde9fb | 1449 | {".htm", CONTENT_HEADER(ContentHTML)}, |
AdamGreen | 0:616601bde9fb | 1450 | {".gif", CONTENT_HEADER(ContentGIF)}, |
AdamGreen | 0:616601bde9fb | 1451 | {".png", CONTENT_HEADER(ContentPNG)}, |
AdamGreen | 0:616601bde9fb | 1452 | {".jpg", CONTENT_HEADER(ContentJPG)}, |
AdamGreen | 0:616601bde9fb | 1453 | {".bmp", CONTENT_HEADER(ContentBMP)}, |
AdamGreen | 0:616601bde9fb | 1454 | {".ico", CONTENT_HEADER(ContentICO)}, |
AdamGreen | 0:616601bde9fb | 1455 | {".class", CONTENT_HEADER(ContentStream)}, |
AdamGreen | 0:616601bde9fb | 1456 | {".cls", CONTENT_HEADER(ContentStream)}, |
AdamGreen | 0:616601bde9fb | 1457 | {".js", CONTENT_HEADER(ContentJScript)}, |
AdamGreen | 0:616601bde9fb | 1458 | {".css", CONTENT_HEADER(ContentCSS)}, |
AdamGreen | 0:616601bde9fb | 1459 | {".swf", CONTENT_HEADER(ContentFlash)}, |
AdamGreen | 0:616601bde9fb | 1460 | {".xml", CONTENT_HEADER(ContentXML)} |
AdamGreen | 0:616601bde9fb | 1461 | }; |
AdamGreen | 0:616601bde9fb | 1462 | // Local variables. |
AdamGreen | 0:616601bde9fb | 1463 | size_t i; |
AdamGreen | 0:616601bde9fb | 1464 | char LocalFilename[HTTP_MAX_FILENAME+1]; |
AdamGreen | 0:616601bde9fb | 1465 | |
AdamGreen | 0:616601bde9fb | 1466 | // Attempt to open the file. |
AdamGreen | 0:616601bde9fb | 1467 | if (0 == strcmp(pURI, "/")) |
AdamGreen | 0:616601bde9fb | 1468 | { |
AdamGreen | 0:616601bde9fb | 1469 | // Iterate through the list of default root filenames. |
AdamGreen | 0:616601bde9fb | 1470 | for (i = 0 ; !m_pFile && i < ARRAYSIZE(g_DefaultFilenames) ; i++) |
AdamGreen | 0:616601bde9fb | 1471 | { |
AdamGreen | 0:616601bde9fb | 1472 | snprintf(LocalFilename, ARRAYSIZE(LocalFilename), "/%s%s", m_pHTTPServer->m_pRootPathname, g_DefaultFilenames[i]); |
AdamGreen | 0:616601bde9fb | 1473 | m_pFile = fopen(LocalFilename, "r"); |
AdamGreen | 0:616601bde9fb | 1474 | } |
AdamGreen | 0:616601bde9fb | 1475 | } |
AdamGreen | 0:616601bde9fb | 1476 | else |
AdamGreen | 0:616601bde9fb | 1477 | { |
AdamGreen | 0:616601bde9fb | 1478 | // Attempt to open the specified URI. |
AdamGreen | 0:616601bde9fb | 1479 | snprintf(LocalFilename, ARRAYSIZE(LocalFilename), "/%s%s", m_pHTTPServer->m_pRootPathname, pURI); |
AdamGreen | 0:616601bde9fb | 1480 | m_pFile = fopen(LocalFilename, "r"); |
AdamGreen | 0:616601bde9fb | 1481 | } |
AdamGreen | 0:616601bde9fb | 1482 | |
AdamGreen | 0:616601bde9fb | 1483 | // Setup the content type field based on whether the file was found or not. |
AdamGreen | 0:616601bde9fb | 1484 | if (!m_pFile) |
AdamGreen | 0:616601bde9fb | 1485 | { |
AdamGreen | 0:616601bde9fb | 1486 | // The file open failed so return a 404 error with HTML. |
AdamGreen | 0:616601bde9fb | 1487 | m_pContentType = ContentHTML; |
AdamGreen | 0:616601bde9fb | 1488 | m_ContentTypeLength = sizeof(ContentHTML) - 1; |
AdamGreen | 0:616601bde9fb | 1489 | m_pErrorHTML = NotFoundHTML; |
AdamGreen | 0:616601bde9fb | 1490 | m_ErrorHTMLLength = sizeof(NotFoundHTML) - 1; |
AdamGreen | 0:616601bde9fb | 1491 | } |
AdamGreen | 0:616601bde9fb | 1492 | else |
AdamGreen | 0:616601bde9fb | 1493 | { |
AdamGreen | 0:616601bde9fb | 1494 | const char* pExtension; |
AdamGreen | 0:616601bde9fb | 1495 | |
AdamGreen | 0:616601bde9fb | 1496 | // Configure the content type header based on filename extension. |
AdamGreen | 0:616601bde9fb | 1497 | pExtension = strrchr(LocalFilename, '.'); |
AdamGreen | 0:616601bde9fb | 1498 | for (i = 0 ; pExtension && i < ARRAYSIZE(ContentMapping) ; i++) |
AdamGreen | 0:616601bde9fb | 1499 | { |
AdamGreen | 0:616601bde9fb | 1500 | if (0 == strcmp(pExtension, ContentMapping[i].pExtension)) |
AdamGreen | 0:616601bde9fb | 1501 | { |
AdamGreen | 0:616601bde9fb | 1502 | m_pContentType = ContentMapping[i].pContentHeader; |
AdamGreen | 0:616601bde9fb | 1503 | m_ContentTypeLength = ContentMapping[i].ContentHeaderLength; |
AdamGreen | 0:616601bde9fb | 1504 | break; |
AdamGreen | 0:616601bde9fb | 1505 | } |
AdamGreen | 0:616601bde9fb | 1506 | } |
AdamGreen | 0:616601bde9fb | 1507 | |
AdamGreen | 0:616601bde9fb | 1508 | // If the type couldn't be found default to plain. |
AdamGreen | 0:616601bde9fb | 1509 | if (!m_pContentType) |
AdamGreen | 0:616601bde9fb | 1510 | { |
AdamGreen | 0:616601bde9fb | 1511 | m_pContentType = ContentDefault; |
AdamGreen | 0:616601bde9fb | 1512 | m_ContentTypeLength = sizeof(ContentDefault) - 1; |
AdamGreen | 0:616601bde9fb | 1513 | } |
AdamGreen | 0:616601bde9fb | 1514 | } |
AdamGreen | 0:616601bde9fb | 1515 | } |
AdamGreen | 0:616601bde9fb | 1516 | |
AdamGreen | 0:616601bde9fb | 1517 | |
AdamGreen | 0:616601bde9fb | 1518 | /* Prepares the headers and content to be sent back to the HTTP client as a |
AdamGreen | 0:616601bde9fb | 1519 | response to its latest request. It then starts sending out the first set of |
AdamGreen | 0:616601bde9fb | 1520 | response packets. |
AdamGreen | 0:616601bde9fb | 1521 | |
AdamGreen | 0:616601bde9fb | 1522 | Parameters: |
AdamGreen | 0:616601bde9fb | 1523 | None. |
AdamGreen | 0:616601bde9fb | 1524 | |
AdamGreen | 0:616601bde9fb | 1525 | Returns: |
AdamGreen | 0:616601bde9fb | 1526 | Nothing. |
AdamGreen | 0:616601bde9fb | 1527 | */ |
AdamGreen | 0:616601bde9fb | 1528 | void CHTTPContext::PrepareResponse() |
AdamGreen | 0:616601bde9fb | 1529 | { |
AdamGreen | 0:616601bde9fb | 1530 | assert ( STATE_DONE == m_ParseState ); |
AdamGreen | 0:616601bde9fb | 1531 | |
AdamGreen | 0:616601bde9fb | 1532 | PrepareResponseHeaders(); |
AdamGreen | 0:616601bde9fb | 1533 | |
AdamGreen | 0:616601bde9fb | 1534 | if (!m_pRequestHandlerContext->BeginResponseContent()) |
AdamGreen | 0:616601bde9fb | 1535 | { |
AdamGreen | 0:616601bde9fb | 1536 | // The application has no content to send so release it. |
AdamGreen | 0:616601bde9fb | 1537 | m_pRequestHandlerContext->Release(); |
AdamGreen | 0:616601bde9fb | 1538 | m_pRequestHandlerContext = NULL; |
AdamGreen | 0:616601bde9fb | 1539 | } |
AdamGreen | 0:616601bde9fb | 1540 | } |
AdamGreen | 0:616601bde9fb | 1541 | |
AdamGreen | 0:616601bde9fb | 1542 | |
AdamGreen | 0:616601bde9fb | 1543 | /* Prepares the headers and content to be sent back to the HTTP client as a |
AdamGreen | 0:616601bde9fb | 1544 | response to its latest request. It then starts sending out the first set of |
AdamGreen | 0:616601bde9fb | 1545 | response packets. |
AdamGreen | 0:616601bde9fb | 1546 | |
AdamGreen | 0:616601bde9fb | 1547 | Parameters: |
AdamGreen | 0:616601bde9fb | 1548 | None. |
AdamGreen | 0:616601bde9fb | 1549 | |
AdamGreen | 0:616601bde9fb | 1550 | Returns: |
AdamGreen | 0:616601bde9fb | 1551 | Nothing. |
AdamGreen | 0:616601bde9fb | 1552 | */ |
AdamGreen | 0:616601bde9fb | 1553 | void CHTTPContext::PrepareResponseHeaders() |
AdamGreen | 0:616601bde9fb | 1554 | { |
AdamGreen | 0:616601bde9fb | 1555 | static const char BlankLine[] = "\r\n"; |
AdamGreen | 0:616601bde9fb | 1556 | const char* pStatusLine = NULL; |
AdamGreen | 0:616601bde9fb | 1557 | const char* pExtraHeaders = NULL; |
AdamGreen | 0:616601bde9fb | 1558 | size_t StatusLineLength = 0; |
AdamGreen | 0:616601bde9fb | 1559 | size_t ExtraHeadersLength = 0; |
AdamGreen | 0:616601bde9fb | 1560 | size_t i = 0; |
AdamGreen | 0:616601bde9fb | 1561 | |
AdamGreen | 0:616601bde9fb | 1562 | if (TYPE_GET_0_9 == m_RequestType) |
AdamGreen | 0:616601bde9fb | 1563 | { |
AdamGreen | 0:616601bde9fb | 1564 | // Don't return any headers for a HTTP/0.9 GET request. |
AdamGreen | 0:616601bde9fb | 1565 | m_HeaderArray[0].pHeaderString = NULL; |
AdamGreen | 0:616601bde9fb | 1566 | m_HeaderArray[0].HeaderLength = 0; |
AdamGreen | 0:616601bde9fb | 1567 | return; |
AdamGreen | 0:616601bde9fb | 1568 | } |
AdamGreen | 0:616601bde9fb | 1569 | |
AdamGreen | 0:616601bde9fb | 1570 | // Prepare the response status. |
AdamGreen | 0:616601bde9fb | 1571 | pStatusLine = m_pRequestHandlerContext->GetStatusLine(&StatusLineLength); |
AdamGreen | 0:616601bde9fb | 1572 | |
AdamGreen | 0:616601bde9fb | 1573 | // Application must return a status line. |
AdamGreen | 0:616601bde9fb | 1574 | assert ( pStatusLine && StatusLineLength > 0 ); |
AdamGreen | 0:616601bde9fb | 1575 | |
AdamGreen | 0:616601bde9fb | 1576 | m_HeaderArray[0].pHeaderString = pStatusLine; |
AdamGreen | 0:616601bde9fb | 1577 | m_HeaderArray[0].HeaderLength = StatusLineLength; |
AdamGreen | 0:616601bde9fb | 1578 | |
AdamGreen | 0:616601bde9fb | 1579 | // The second header returned will always be the server type, no |
AdamGreen | 0:616601bde9fb | 1580 | // matter what response we send back. |
AdamGreen | 0:616601bde9fb | 1581 | m_HeaderArray[1].pHeaderString = m_pHTTPServer->m_ServerHeader; |
AdamGreen | 0:616601bde9fb | 1582 | m_HeaderArray[1].HeaderLength = m_pHTTPServer->m_ServerHeaderLength; |
AdamGreen | 0:616601bde9fb | 1583 | |
AdamGreen | 0:616601bde9fb | 1584 | // Find out what other headers the application would like to send back to |
AdamGreen | 0:616601bde9fb | 1585 | // the HTTP client. |
AdamGreen | 0:616601bde9fb | 1586 | pExtraHeaders = m_pRequestHandlerContext->GetResponseHeaders(&ExtraHeadersLength); |
AdamGreen | 0:616601bde9fb | 1587 | if (pExtraHeaders) |
AdamGreen | 0:616601bde9fb | 1588 | { |
AdamGreen | 0:616601bde9fb | 1589 | m_HeaderArray[2].pHeaderString = pExtraHeaders; |
AdamGreen | 0:616601bde9fb | 1590 | m_HeaderArray[2].HeaderLength = ExtraHeadersLength; |
AdamGreen | 0:616601bde9fb | 1591 | i = 3; |
AdamGreen | 0:616601bde9fb | 1592 | } |
AdamGreen | 0:616601bde9fb | 1593 | else |
AdamGreen | 0:616601bde9fb | 1594 | { |
AdamGreen | 0:616601bde9fb | 1595 | // Won't be using all of the elements of m_HeaderArray. |
AdamGreen | 0:616601bde9fb | 1596 | i = 2; |
AdamGreen | 0:616601bde9fb | 1597 | } |
AdamGreen | 0:616601bde9fb | 1598 | |
AdamGreen | 0:616601bde9fb | 1599 | // Finally send the blank line header which separates the headers from the |
AdamGreen | 0:616601bde9fb | 1600 | // entity body. |
AdamGreen | 0:616601bde9fb | 1601 | m_HeaderArray[i].pHeaderString = BlankLine; |
AdamGreen | 0:616601bde9fb | 1602 | m_HeaderArray[i].HeaderLength = sizeof(BlankLine) -1; |
AdamGreen | 0:616601bde9fb | 1603 | } |
AdamGreen | 0:616601bde9fb | 1604 | |
AdamGreen | 0:616601bde9fb | 1605 | /* Attempts to allocate a SBuffer object for reading the data into. If such a |
AdamGreen | 0:616601bde9fb | 1606 | buffer is available then read HTTP headers and file data into the buffer. |
AdamGreen | 0:616601bde9fb | 1607 | If the buffer can't be allocated, then add this client to a queue to be |
AdamGreen | 0:616601bde9fb | 1608 | processed later. |
AdamGreen | 0:616601bde9fb | 1609 | |
AdamGreen | 0:616601bde9fb | 1610 | Parameters: |
AdamGreen | 0:616601bde9fb | 1611 | None. |
AdamGreen | 0:616601bde9fb | 1612 | |
AdamGreen | 0:616601bde9fb | 1613 | Returns: |
AdamGreen | 0:616601bde9fb | 1614 | Nothing. |
AdamGreen | 0:616601bde9fb | 1615 | */ |
AdamGreen | 0:616601bde9fb | 1616 | void CHTTPContext::ReadContent() |
AdamGreen | 0:616601bde9fb | 1617 | { |
AdamGreen | 0:616601bde9fb | 1618 | // Local variables. |
AdamGreen | 0:616601bde9fb | 1619 | SBuffer* pWriteBuffer = m_BufferQueue[m_BufferQueueHead]; |
AdamGreen | 0:616601bde9fb | 1620 | unsigned int BytesLeftInBuffer; |
AdamGreen | 0:616601bde9fb | 1621 | char* pData; |
AdamGreen | 0:616601bde9fb | 1622 | unsigned int i; |
AdamGreen | 0:616601bde9fb | 1623 | |
AdamGreen | 0:616601bde9fb | 1624 | |
AdamGreen | 0:616601bde9fb | 1625 | // Return now if there is already a buffer filled with data in the process |
AdamGreen | 0:616601bde9fb | 1626 | // of being written out to the connection. Also return if there is no more |
AdamGreen | 0:616601bde9fb | 1627 | // data to be sent to this client. |
AdamGreen | 0:616601bde9fb | 1628 | if (pWriteBuffer || |
AdamGreen | 0:616601bde9fb | 1629 | (m_HeaderIndex >= ARRAYSIZE(m_HeaderArray) && |
AdamGreen | 0:616601bde9fb | 1630 | !m_pRequestHandlerContext)) |
AdamGreen | 0:616601bde9fb | 1631 | { |
AdamGreen | 0:616601bde9fb | 1632 | return; |
AdamGreen | 0:616601bde9fb | 1633 | } |
AdamGreen | 0:616601bde9fb | 1634 | |
AdamGreen | 0:616601bde9fb | 1635 | // Attempt to allocate a new SBuffer. |
AdamGreen | 0:616601bde9fb | 1636 | pWriteBuffer = m_pHTTPServer->AllocateBuffer(this); |
AdamGreen | 0:616601bde9fb | 1637 | if (!pWriteBuffer) |
AdamGreen | 0:616601bde9fb | 1638 | { |
AdamGreen | 0:616601bde9fb | 1639 | // There is no buffer available at this time. Will be called back |
AdamGreen | 0:616601bde9fb | 1640 | // later when one becomes available. |
AdamGreen | 0:616601bde9fb | 1641 | return; |
AdamGreen | 0:616601bde9fb | 1642 | } |
AdamGreen | 0:616601bde9fb | 1643 | assert ( 0 == pWriteBuffer->BytesToWrite ); |
AdamGreen | 0:616601bde9fb | 1644 | assert ( 0 == pWriteBuffer->UnacknowledgedBytes ); |
AdamGreen | 0:616601bde9fb | 1645 | |
AdamGreen | 0:616601bde9fb | 1646 | // Buffer is free. Now fill it with headers and file data. |
AdamGreen | 0:616601bde9fb | 1647 | BytesLeftInBuffer = sizeof(pWriteBuffer->Data); |
AdamGreen | 0:616601bde9fb | 1648 | pData = pWriteBuffer->Data; |
AdamGreen | 0:616601bde9fb | 1649 | pWriteBuffer->pWrite = pData; |
AdamGreen | 0:616601bde9fb | 1650 | |
AdamGreen | 0:616601bde9fb | 1651 | // Copy remaining headers into free buffer. |
AdamGreen | 0:616601bde9fb | 1652 | for (i = m_HeaderIndex ; |
AdamGreen | 0:616601bde9fb | 1653 | i < ARRAYSIZE(m_HeaderArray) ; |
AdamGreen | 0:616601bde9fb | 1654 | i++) |
AdamGreen | 0:616601bde9fb | 1655 | { |
AdamGreen | 0:616601bde9fb | 1656 | unsigned int BytesToCopy; |
AdamGreen | 0:616601bde9fb | 1657 | unsigned int HeaderBytesLeft; |
AdamGreen | 0:616601bde9fb | 1658 | |
AdamGreen | 0:616601bde9fb | 1659 | assert ( i == m_HeaderIndex ); |
AdamGreen | 0:616601bde9fb | 1660 | |
AdamGreen | 0:616601bde9fb | 1661 | if (!m_HeaderArray[i].pHeaderString) |
AdamGreen | 0:616601bde9fb | 1662 | { |
AdamGreen | 0:616601bde9fb | 1663 | // Not all (or any) of the headers were used so mark as done and |
AdamGreen | 0:616601bde9fb | 1664 | // no need to copy any more so break out of loop. |
AdamGreen | 0:616601bde9fb | 1665 | m_HeaderIndex = ARRAYSIZE(m_HeaderArray); |
AdamGreen | 0:616601bde9fb | 1666 | break; |
AdamGreen | 0:616601bde9fb | 1667 | } |
AdamGreen | 0:616601bde9fb | 1668 | |
AdamGreen | 0:616601bde9fb | 1669 | // Determine how many bytes are to be copied. |
AdamGreen | 0:616601bde9fb | 1670 | HeaderBytesLeft = m_HeaderArray[i].HeaderLength - |
AdamGreen | 0:616601bde9fb | 1671 | m_HeaderOffset; |
AdamGreen | 0:616601bde9fb | 1672 | if (HeaderBytesLeft > BytesLeftInBuffer) |
AdamGreen | 0:616601bde9fb | 1673 | { |
AdamGreen | 0:616601bde9fb | 1674 | BytesToCopy = BytesLeftInBuffer; |
AdamGreen | 0:616601bde9fb | 1675 | } |
AdamGreen | 0:616601bde9fb | 1676 | else |
AdamGreen | 0:616601bde9fb | 1677 | { |
AdamGreen | 0:616601bde9fb | 1678 | BytesToCopy = HeaderBytesLeft; |
AdamGreen | 0:616601bde9fb | 1679 | } |
AdamGreen | 0:616601bde9fb | 1680 | |
AdamGreen | 0:616601bde9fb | 1681 | // Perform the copy. |
AdamGreen | 0:616601bde9fb | 1682 | memcpy(pData, |
AdamGreen | 0:616601bde9fb | 1683 | m_HeaderArray[i].pHeaderString + m_HeaderOffset, |
AdamGreen | 0:616601bde9fb | 1684 | BytesToCopy); |
AdamGreen | 0:616601bde9fb | 1685 | |
AdamGreen | 0:616601bde9fb | 1686 | // Update buffer information. |
AdamGreen | 0:616601bde9fb | 1687 | pData += BytesToCopy; |
AdamGreen | 0:616601bde9fb | 1688 | BytesLeftInBuffer -= BytesToCopy; |
AdamGreen | 0:616601bde9fb | 1689 | HeaderBytesLeft -= BytesToCopy; |
AdamGreen | 0:616601bde9fb | 1690 | if (0 == HeaderBytesLeft) |
AdamGreen | 0:616601bde9fb | 1691 | { |
AdamGreen | 0:616601bde9fb | 1692 | // Advance to next header. |
AdamGreen | 0:616601bde9fb | 1693 | m_HeaderIndex++; |
AdamGreen | 0:616601bde9fb | 1694 | m_HeaderOffset = 0; |
AdamGreen | 0:616601bde9fb | 1695 | } |
AdamGreen | 0:616601bde9fb | 1696 | else |
AdamGreen | 0:616601bde9fb | 1697 | { |
AdamGreen | 0:616601bde9fb | 1698 | // Advance offset within this header and exit since buffer is full. |
AdamGreen | 0:616601bde9fb | 1699 | assert ( 0 == BytesLeftInBuffer ); |
AdamGreen | 0:616601bde9fb | 1700 | |
AdamGreen | 0:616601bde9fb | 1701 | m_HeaderOffset += BytesToCopy; |
AdamGreen | 0:616601bde9fb | 1702 | break; |
AdamGreen | 0:616601bde9fb | 1703 | } |
AdamGreen | 0:616601bde9fb | 1704 | } |
AdamGreen | 0:616601bde9fb | 1705 | |
AdamGreen | 0:616601bde9fb | 1706 | // Copy file data into buffer for sending. |
AdamGreen | 0:616601bde9fb | 1707 | if (m_pRequestHandlerContext) |
AdamGreen | 0:616601bde9fb | 1708 | { |
AdamGreen | 0:616601bde9fb | 1709 | size_t BytesRead; |
AdamGreen | 0:616601bde9fb | 1710 | |
AdamGreen | 0:616601bde9fb | 1711 | BytesRead = m_pRequestHandlerContext->ReadResponseContentBlock(pData, BytesLeftInBuffer); |
AdamGreen | 0:616601bde9fb | 1712 | if (BytesRead < BytesLeftInBuffer) |
AdamGreen | 0:616601bde9fb | 1713 | { |
AdamGreen | 0:616601bde9fb | 1714 | // Must have hit end of file, so close it. |
AdamGreen | 0:616601bde9fb | 1715 | m_pRequestHandlerContext->EndResponseContent(); |
AdamGreen | 0:616601bde9fb | 1716 | m_pRequestHandlerContext->Release(); |
AdamGreen | 0:616601bde9fb | 1717 | m_pRequestHandlerContext = NULL; |
AdamGreen | 0:616601bde9fb | 1718 | } |
AdamGreen | 0:616601bde9fb | 1719 | pData += BytesRead; |
AdamGreen | 0:616601bde9fb | 1720 | BytesLeftInBuffer -= BytesRead; |
AdamGreen | 0:616601bde9fb | 1721 | } |
AdamGreen | 0:616601bde9fb | 1722 | |
AdamGreen | 0:616601bde9fb | 1723 | // Update the buffer valid byte count. |
AdamGreen | 0:616601bde9fb | 1724 | pWriteBuffer->BytesToWrite = ARRAYSIZE(pWriteBuffer->Data) - |
AdamGreen | 0:616601bde9fb | 1725 | BytesLeftInBuffer; |
AdamGreen | 0:616601bde9fb | 1726 | |
AdamGreen | 0:616601bde9fb | 1727 | // Update the client context to indicate that it now has a buffer ready to |
AdamGreen | 0:616601bde9fb | 1728 | // be written out to the client connection. |
AdamGreen | 0:616601bde9fb | 1729 | m_BufferQueue[m_BufferQueueHead] = pWriteBuffer; |
AdamGreen | 0:616601bde9fb | 1730 | } |
AdamGreen | 0:616601bde9fb | 1731 | |
AdamGreen | 0:616601bde9fb | 1732 | |
AdamGreen | 0:616601bde9fb | 1733 | /* Attempts to send HTTP client data already read into buffer and also reads |
AdamGreen | 0:616601bde9fb | 1734 | in more data to the buffer for sending as necessary. This data will be |
AdamGreen | 0:616601bde9fb | 1735 | composed of HTTP headers and file data. |
AdamGreen | 0:616601bde9fb | 1736 | |
AdamGreen | 0:616601bde9fb | 1737 | Parameters: |
AdamGreen | 0:616601bde9fb | 1738 | None. |
AdamGreen | 0:616601bde9fb | 1739 | |
AdamGreen | 0:616601bde9fb | 1740 | Returns: |
AdamGreen | 0:616601bde9fb | 1741 | Nothing. |
AdamGreen | 0:616601bde9fb | 1742 | */ |
AdamGreen | 0:616601bde9fb | 1743 | void CHTTPContext::SendResponse() |
AdamGreen | 0:616601bde9fb | 1744 | { |
AdamGreen | 0:616601bde9fb | 1745 | int ConnectionClosed; |
AdamGreen | 0:616601bde9fb | 1746 | |
AdamGreen | 0:616601bde9fb | 1747 | // Attempt to send any data still in write buffer. |
AdamGreen | 0:616601bde9fb | 1748 | ConnectionClosed = WriteData(); |
AdamGreen | 0:616601bde9fb | 1749 | |
AdamGreen | 0:616601bde9fb | 1750 | // Return now if the connection was closed. |
AdamGreen | 0:616601bde9fb | 1751 | if (ConnectionClosed) |
AdamGreen | 0:616601bde9fb | 1752 | { |
AdamGreen | 0:616601bde9fb | 1753 | return; |
AdamGreen | 0:616601bde9fb | 1754 | } |
AdamGreen | 0:616601bde9fb | 1755 | |
AdamGreen | 0:616601bde9fb | 1756 | // Read in more content if possible. |
AdamGreen | 0:616601bde9fb | 1757 | ReadContent(); |
AdamGreen | 0:616601bde9fb | 1758 | |
AdamGreen | 0:616601bde9fb | 1759 | // UNDONE: Once I have a performance test available, try removing this to see if it has any impact on performance. |
AdamGreen | 0:616601bde9fb | 1760 | // Also want to wait for DMA based driver. |
AdamGreen | 0:616601bde9fb | 1761 | // Attempt to send data that was just read into buffer. |
AdamGreen | 0:616601bde9fb | 1762 | WriteData(); |
AdamGreen | 0:616601bde9fb | 1763 | } |
AdamGreen | 0:616601bde9fb | 1764 | |
AdamGreen | 0:616601bde9fb | 1765 | |
AdamGreen | 0:616601bde9fb | 1766 | /* Sends any remaining response data to the HTTP client and closes the |
AdamGreen | 0:616601bde9fb | 1767 | connection once the data has been sent. |
AdamGreen | 0:616601bde9fb | 1768 | |
AdamGreen | 0:616601bde9fb | 1769 | Parameters: |
AdamGreen | 0:616601bde9fb | 1770 | None. |
AdamGreen | 0:616601bde9fb | 1771 | |
AdamGreen | 0:616601bde9fb | 1772 | Returns: |
AdamGreen | 0:616601bde9fb | 1773 | 1 if the last chunk was sent and the connection closed and 0 otherwise. |
AdamGreen | 0:616601bde9fb | 1774 | */ |
AdamGreen | 0:616601bde9fb | 1775 | int CHTTPContext::WriteData() |
AdamGreen | 0:616601bde9fb | 1776 | { |
AdamGreen | 0:616601bde9fb | 1777 | // Local variables. |
AdamGreen | 0:616601bde9fb | 1778 | err_t WriteResult = ERR_MEM; |
AdamGreen | 0:616601bde9fb | 1779 | size_t BytesToWrite; |
AdamGreen | 0:616601bde9fb | 1780 | size_t SendBufferSize; |
AdamGreen | 0:616601bde9fb | 1781 | SBuffer* pWriteBuffer = m_BufferQueue[m_BufferQueueHead]; |
AdamGreen | 0:616601bde9fb | 1782 | |
AdamGreen | 0:616601bde9fb | 1783 | // Just return if there isn't a write buffer with data to send. |
AdamGreen | 0:616601bde9fb | 1784 | if (!pWriteBuffer) |
AdamGreen | 0:616601bde9fb | 1785 | { |
AdamGreen | 0:616601bde9fb | 1786 | return 0; |
AdamGreen | 0:616601bde9fb | 1787 | } |
AdamGreen | 0:616601bde9fb | 1788 | |
AdamGreen | 0:616601bde9fb | 1789 | // Don't try to send more bytes than what the connection will accept. |
AdamGreen | 0:616601bde9fb | 1790 | BytesToWrite = pWriteBuffer->BytesToWrite; |
AdamGreen | 0:616601bde9fb | 1791 | SendBufferSize = tcp_sndbuf(m_pPCB); |
AdamGreen | 0:616601bde9fb | 1792 | if (BytesToWrite > SendBufferSize) |
AdamGreen | 0:616601bde9fb | 1793 | { |
AdamGreen | 0:616601bde9fb | 1794 | BytesToWrite = SendBufferSize; |
AdamGreen | 0:616601bde9fb | 1795 | } |
AdamGreen | 0:616601bde9fb | 1796 | |
AdamGreen | 0:616601bde9fb | 1797 | // Keep attempting to send response data and shrinking the write until it |
AdamGreen | 0:616601bde9fb | 1798 | // succeeds or it shrinks down to 0 bytes. |
AdamGreen | 0:616601bde9fb | 1799 | while (BytesToWrite > 0) |
AdamGreen | 0:616601bde9fb | 1800 | { |
AdamGreen | 0:616601bde9fb | 1801 | // Attempt to write the response data out to the PCB. |
AdamGreen | 0:616601bde9fb | 1802 | WriteResult = tcp_write(m_pPCB, |
AdamGreen | 0:616601bde9fb | 1803 | pWriteBuffer->pWrite, |
AdamGreen | 0:616601bde9fb | 1804 | BytesToWrite, |
AdamGreen | 0:616601bde9fb | 1805 | 0); |
AdamGreen | 0:616601bde9fb | 1806 | if (ERR_OK == WriteResult) |
AdamGreen | 0:616601bde9fb | 1807 | { |
AdamGreen | 0:616601bde9fb | 1808 | // Write was successful so update buffer descriptors in the context |
AdamGreen | 0:616601bde9fb | 1809 | // objects. |
AdamGreen | 0:616601bde9fb | 1810 | pWriteBuffer->pWrite += BytesToWrite; |
AdamGreen | 0:616601bde9fb | 1811 | pWriteBuffer->BytesToWrite -= BytesToWrite; |
AdamGreen | 0:616601bde9fb | 1812 | pWriteBuffer->UnacknowledgedBytes += BytesToWrite; |
AdamGreen | 0:616601bde9fb | 1813 | |
AdamGreen | 0:616601bde9fb | 1814 | // Successfully queued up data for sending so reset retry count. |
AdamGreen | 0:616601bde9fb | 1815 | m_RetryCount = 0; |
AdamGreen | 0:616601bde9fb | 1816 | break; |
AdamGreen | 0:616601bde9fb | 1817 | } |
AdamGreen | 0:616601bde9fb | 1818 | else if (ERR_MEM == WriteResult) |
AdamGreen | 0:616601bde9fb | 1819 | { |
AdamGreen | 0:616601bde9fb | 1820 | // Failed to perform the write due to out of memory error. |
AdamGreen | 0:616601bde9fb | 1821 | // Check to see if the send queue if full. If not, half the write |
AdamGreen | 0:616601bde9fb | 1822 | // size and try again. |
AdamGreen | 0:616601bde9fb | 1823 | if (0 == tcp_sndqueuelen(m_pPCB)) |
AdamGreen | 0:616601bde9fb | 1824 | { |
AdamGreen | 0:616601bde9fb | 1825 | break; |
AdamGreen | 0:616601bde9fb | 1826 | } |
AdamGreen | 0:616601bde9fb | 1827 | // UNDONE: Rather than half it, subtract the size of a MSS. |
AdamGreen | 0:616601bde9fb | 1828 | BytesToWrite >>= 1; |
AdamGreen | 0:616601bde9fb | 1829 | } |
AdamGreen | 0:616601bde9fb | 1830 | else |
AdamGreen | 0:616601bde9fb | 1831 | { |
AdamGreen | 0:616601bde9fb | 1832 | // Received unexpected error. |
AdamGreen | 0:616601bde9fb | 1833 | break; |
AdamGreen | 0:616601bde9fb | 1834 | } |
AdamGreen | 0:616601bde9fb | 1835 | } |
AdamGreen | 0:616601bde9fb | 1836 | |
AdamGreen | 0:616601bde9fb | 1837 | if (0 == pWriteBuffer->BytesToWrite) |
AdamGreen | 0:616601bde9fb | 1838 | { |
AdamGreen | 0:616601bde9fb | 1839 | // There are no more bytes to write from the buffer so advance the |
AdamGreen | 0:616601bde9fb | 1840 | // header pointer to the next item in the buffer queue. |
AdamGreen | 0:616601bde9fb | 1841 | m_BufferQueueHead = (m_BufferQueueHead + 1) & 1; |
AdamGreen | 0:616601bde9fb | 1842 | |
AdamGreen | 0:616601bde9fb | 1843 | // If there are no more bytes to send and the client has finished sending |
AdamGreen | 0:616601bde9fb | 1844 | // its request then we should attempt to close the connection. |
AdamGreen | 0:616601bde9fb | 1845 | if (m_HeaderIndex >= ARRAYSIZE(m_HeaderArray) && |
AdamGreen | 0:616601bde9fb | 1846 | !m_pRequestHandlerContext && |
AdamGreen | 0:616601bde9fb | 1847 | STATE_DONE == m_ParseState) |
AdamGreen | 0:616601bde9fb | 1848 | { |
AdamGreen | 0:616601bde9fb | 1849 | TRACE("HTTP: Response send completed to: "); |
AdamGreen | 0:616601bde9fb | 1850 | NetworkPrintRemoteAddress(m_pPCB); |
AdamGreen | 0:616601bde9fb | 1851 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 1852 | CloseConnection(); |
AdamGreen | 0:616601bde9fb | 1853 | |
AdamGreen | 0:616601bde9fb | 1854 | return 1; |
AdamGreen | 0:616601bde9fb | 1855 | } |
AdamGreen | 0:616601bde9fb | 1856 | } |
AdamGreen | 0:616601bde9fb | 1857 | |
AdamGreen | 0:616601bde9fb | 1858 | return 0; |
AdamGreen | 0:616601bde9fb | 1859 | } |
AdamGreen | 0:616601bde9fb | 1860 | |
AdamGreen | 0:616601bde9fb | 1861 | |
AdamGreen | 0:616601bde9fb | 1862 | /* Called for a context which was waiting for a buffer when a buffer is freed |
AdamGreen | 0:616601bde9fb | 1863 | by another connection. |
AdamGreen | 0:616601bde9fb | 1864 | |
AdamGreen | 0:616601bde9fb | 1865 | Parameters: |
AdamGreen | 0:616601bde9fb | 1866 | None. |
AdamGreen | 0:616601bde9fb | 1867 | |
AdamGreen | 0:616601bde9fb | 1868 | Returns: |
AdamGreen | 0:616601bde9fb | 1869 | Nothing. |
AdamGreen | 0:616601bde9fb | 1870 | */ |
AdamGreen | 0:616601bde9fb | 1871 | void CHTTPContext::BufferAvailable() |
AdamGreen | 0:616601bde9fb | 1872 | { |
AdamGreen | 0:616601bde9fb | 1873 | // Allow the context to allocate the buffer and fill it in with data. |
AdamGreen | 0:616601bde9fb | 1874 | ReadContent(); |
AdamGreen | 0:616601bde9fb | 1875 | |
AdamGreen | 0:616601bde9fb | 1876 | // Allow the context to send the data that was just read into the |
AdamGreen | 0:616601bde9fb | 1877 | // buffer. |
AdamGreen | 0:616601bde9fb | 1878 | SendResponse(); |
AdamGreen | 0:616601bde9fb | 1879 | |
AdamGreen | 0:616601bde9fb | 1880 | // Tell lwIP to send the segments for this PCB as it normally only |
AdamGreen | 0:616601bde9fb | 1881 | // queues up segments automatically for the PCB whose callback is |
AdamGreen | 0:616601bde9fb | 1882 | // currently being called. |
AdamGreen | 0:616601bde9fb | 1883 | tcp_output(m_pPCB); |
AdamGreen | 0:616601bde9fb | 1884 | } |
AdamGreen | 0:616601bde9fb | 1885 | |
AdamGreen | 0:616601bde9fb | 1886 | |
AdamGreen | 0:616601bde9fb | 1887 | |
AdamGreen | 0:616601bde9fb | 1888 | /* Constructor */ |
AdamGreen | 0:616601bde9fb | 1889 | CHTTPServer::CHTTPServer() |
AdamGreen | 0:616601bde9fb | 1890 | { |
AdamGreen | 0:616601bde9fb | 1891 | m_pHTTPListenPCB = NULL; |
AdamGreen | 0:616601bde9fb | 1892 | m_pContextWaitingHead = NULL; |
AdamGreen | 0:616601bde9fb | 1893 | m_pContextWaitingTail = NULL; |
AdamGreen | 0:616601bde9fb | 1894 | m_pRootPathname = NULL; |
AdamGreen | 0:616601bde9fb | 1895 | m_pRequestHandler = NULL; |
AdamGreen | 0:616601bde9fb | 1896 | m_ServerHeaderLength = 0;; |
AdamGreen | 0:616601bde9fb | 1897 | m_ServerHeader[0] = '\0'; |
AdamGreen | 0:616601bde9fb | 1898 | memset(m_BufferPool, 0, sizeof(m_BufferPool)); |
AdamGreen | 0:616601bde9fb | 1899 | } |
AdamGreen | 0:616601bde9fb | 1900 | |
AdamGreen | 0:616601bde9fb | 1901 | |
AdamGreen | 0:616601bde9fb | 1902 | /* Attach IRequestHandler to the HTTP server to allow application to add |
AdamGreen | 0:616601bde9fb | 1903 | custom GET/POST handling. |
AdamGreen | 0:616601bde9fb | 1904 | |
AdamGreen | 0:616601bde9fb | 1905 | Parameters: |
AdamGreen | 0:616601bde9fb | 1906 | pHandler is a pointer to the application specific request handler object |
AdamGreen | 0:616601bde9fb | 1907 | to be used by the HTTP server when it receives GET/POST requests. |
AdamGreen | 0:616601bde9fb | 1908 | |
AdamGreen | 0:616601bde9fb | 1909 | Returns: |
AdamGreen | 0:616601bde9fb | 1910 | Returns 0 on successful attachment and a positive value otherwise. |
AdamGreen | 0:616601bde9fb | 1911 | */ |
AdamGreen | 0:616601bde9fb | 1912 | int CHTTPServer::AttachRequestHandler(IHTTPRequestHandler* pHandler) |
AdamGreen | 0:616601bde9fb | 1913 | { |
AdamGreen | 0:616601bde9fb | 1914 | m_pRequestHandler = pHandler; |
AdamGreen | 0:616601bde9fb | 1915 | |
AdamGreen | 0:616601bde9fb | 1916 | return 0; |
AdamGreen | 0:616601bde9fb | 1917 | } |
AdamGreen | 0:616601bde9fb | 1918 | |
AdamGreen | 0:616601bde9fb | 1919 | |
AdamGreen | 0:616601bde9fb | 1920 | /* Initializes the HTTP server using the raw lwIP APIs. This consists of |
AdamGreen | 0:616601bde9fb | 1921 | creating a PCB (low level lwIP socket), binding it to port 80 and then |
AdamGreen | 0:616601bde9fb | 1922 | listening for connections on this port. When subsequent connection comes |
AdamGreen | 0:616601bde9fb | 1923 | into the HTTP server, a callback will be made into the HTTPAccept() |
AdamGreen | 0:616601bde9fb | 1924 | function that is registered here as well. |
AdamGreen | 0:616601bde9fb | 1925 | |
AdamGreen | 0:616601bde9fb | 1926 | Parameters: |
AdamGreen | 0:616601bde9fb | 1927 | pRootPathame is the pathname of the default root directory from which the |
AdamGreen | 0:616601bde9fb | 1928 | HTTP GET requests are satisfied. |
AdamGreen | 0:616601bde9fb | 1929 | pServerName is the name of the server to be returned to the HTTP client |
AdamGreen | 0:616601bde9fb | 1930 | in the Server header. |
AdamGreen | 0:616601bde9fb | 1931 | BindPort is the TCP/IP port to which the HTTP server should listen for |
AdamGreen | 0:616601bde9fb | 1932 | incoming requests. The default port for HTTP would be 80. |
AdamGreen | 0:616601bde9fb | 1933 | |
AdamGreen | 0:616601bde9fb | 1934 | Returns: |
AdamGreen | 0:616601bde9fb | 1935 | 0 on success and a positive error code otherwise. |
AdamGreen | 0:616601bde9fb | 1936 | */ |
AdamGreen | 0:616601bde9fb | 1937 | int CHTTPServer::Bind(const char* pRootPathname, |
AdamGreen | 0:616601bde9fb | 1938 | const char* pServerName, |
AdamGreen | 0:616601bde9fb | 1939 | unsigned short BindPort) |
AdamGreen | 0:616601bde9fb | 1940 | { |
AdamGreen | 0:616601bde9fb | 1941 | int Return = 1; |
AdamGreen | 0:616601bde9fb | 1942 | tcp_pcb* pcb = NULL; |
AdamGreen | 0:616601bde9fb | 1943 | int Length = -1; |
AdamGreen | 0:616601bde9fb | 1944 | err_t Result; |
AdamGreen | 0:616601bde9fb | 1945 | |
AdamGreen | 0:616601bde9fb | 1946 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 1947 | assert ( pRootPathname && pServerName ); |
AdamGreen | 0:616601bde9fb | 1948 | |
AdamGreen | 0:616601bde9fb | 1949 | // Create server header string. |
AdamGreen | 0:616601bde9fb | 1950 | Length = snprintf(m_ServerHeader, ARRAYSIZE(m_ServerHeader), "Server: %s\r\n", pServerName); |
AdamGreen | 0:616601bde9fb | 1951 | if (Length < 0 || Length >= (int)ARRAYSIZE(m_ServerHeader)) |
AdamGreen | 0:616601bde9fb | 1952 | { |
AdamGreen | 0:616601bde9fb | 1953 | printf("error: '%s' is too long for the HTTP server name string.\r\n", pServerName); |
AdamGreen | 0:616601bde9fb | 1954 | goto Error; |
AdamGreen | 0:616601bde9fb | 1955 | } |
AdamGreen | 0:616601bde9fb | 1956 | m_ServerHeaderLength = (size_t)Length; |
AdamGreen | 0:616601bde9fb | 1957 | |
AdamGreen | 0:616601bde9fb | 1958 | // Save away root directory name. |
AdamGreen | 0:616601bde9fb | 1959 | m_pRootPathname = pRootPathname; |
AdamGreen | 0:616601bde9fb | 1960 | |
AdamGreen | 0:616601bde9fb | 1961 | // Create the PCB (low level lwIP socket) for the HTTP server to listen to |
AdamGreen | 0:616601bde9fb | 1962 | // on well known HTTP port 80. |
AdamGreen | 0:616601bde9fb | 1963 | pcb = tcp_new(); |
AdamGreen | 0:616601bde9fb | 1964 | if (!pcb) |
AdamGreen | 0:616601bde9fb | 1965 | { |
AdamGreen | 0:616601bde9fb | 1966 | printf("error: Failed to create new connection for HTTP server to listen upon.\r\n"); |
AdamGreen | 0:616601bde9fb | 1967 | goto Error; |
AdamGreen | 0:616601bde9fb | 1968 | } |
AdamGreen | 0:616601bde9fb | 1969 | |
AdamGreen | 0:616601bde9fb | 1970 | // Bind the PCB to port 80. |
AdamGreen | 0:616601bde9fb | 1971 | Result = tcp_bind(pcb, IP_ADDR_ANY, BindPort); |
AdamGreen | 0:616601bde9fb | 1972 | if (ERR_OK != Result) |
AdamGreen | 0:616601bde9fb | 1973 | { |
AdamGreen | 0:616601bde9fb | 1974 | printf("error: HTTP server failed to bind to port %d.\r\n", BindPort); |
AdamGreen | 0:616601bde9fb | 1975 | goto Error; |
AdamGreen | 0:616601bde9fb | 1976 | } |
AdamGreen | 0:616601bde9fb | 1977 | |
AdamGreen | 0:616601bde9fb | 1978 | // Listen on this PCB. |
AdamGreen | 0:616601bde9fb | 1979 | m_pHTTPListenPCB = tcp_listen(pcb); |
AdamGreen | 0:616601bde9fb | 1980 | if (!m_pHTTPListenPCB) |
AdamGreen | 0:616601bde9fb | 1981 | { |
AdamGreen | 0:616601bde9fb | 1982 | printf("error: HTTP server failed to listen on port %d.\r\n", BindPort); |
AdamGreen | 0:616601bde9fb | 1983 | goto Error; |
AdamGreen | 0:616601bde9fb | 1984 | } |
AdamGreen | 0:616601bde9fb | 1985 | |
AdamGreen | 0:616601bde9fb | 1986 | // pcb was freed by successful tcp_listen() call. |
AdamGreen | 0:616601bde9fb | 1987 | pcb = NULL; |
AdamGreen | 0:616601bde9fb | 1988 | |
AdamGreen | 0:616601bde9fb | 1989 | // When connections come in on port 80, we want lwIP to call our |
AdamGreen | 0:616601bde9fb | 1990 | // HTTPAccept() function to handle the connection. |
AdamGreen | 0:616601bde9fb | 1991 | tcp_arg(m_pHTTPListenPCB, this); |
AdamGreen | 0:616601bde9fb | 1992 | tcp_accept(m_pHTTPListenPCB, HTTPAccept); |
AdamGreen | 0:616601bde9fb | 1993 | |
AdamGreen | 0:616601bde9fb | 1994 | // Return to the main program since there is nothing left to do until a |
AdamGreen | 0:616601bde9fb | 1995 | // client tries to make a connection on port 80, at which time lwIP will |
AdamGreen | 0:616601bde9fb | 1996 | // get the connection packet from the Ethernet driver and make the callback |
AdamGreen | 0:616601bde9fb | 1997 | // into HTTPAccept(). |
AdamGreen | 0:616601bde9fb | 1998 | |
AdamGreen | 0:616601bde9fb | 1999 | Return = 0; |
AdamGreen | 0:616601bde9fb | 2000 | Error: |
AdamGreen | 0:616601bde9fb | 2001 | if (pcb) |
AdamGreen | 0:616601bde9fb | 2002 | { |
AdamGreen | 0:616601bde9fb | 2003 | tcp_close(pcb); |
AdamGreen | 0:616601bde9fb | 2004 | } |
AdamGreen | 0:616601bde9fb | 2005 | return Return; |
AdamGreen | 0:616601bde9fb | 2006 | } |
AdamGreen | 0:616601bde9fb | 2007 | |
AdamGreen | 0:616601bde9fb | 2008 | |
AdamGreen | 0:616601bde9fb | 2009 | /* Called by lwIP whenever a connection is made on the PCB which is listening |
AdamGreen | 0:616601bde9fb | 2010 | on port 80 for HTTP requests. The main task of this callback is to create |
AdamGreen | 0:616601bde9fb | 2011 | any necessary state to track this unique HTTP client connection and then |
AdamGreen | 0:616601bde9fb | 2012 | setup the callbacks to be called when activity occurs on this client |
AdamGreen | 0:616601bde9fb | 2013 | connection. |
AdamGreen | 0:616601bde9fb | 2014 | |
AdamGreen | 0:616601bde9fb | 2015 | Parameters: |
AdamGreen | 0:616601bde9fb | 2016 | pvArg is the value set on the listening PCB through the call to tcp_arg in |
AdamGreen | 0:616601bde9fb | 2017 | HTTPInit() which is a pointer to the intitialized network object. |
AdamGreen | 0:616601bde9fb | 2018 | pNewPCB is a pointer to the new PCB that was just allocated by the lwIP |
AdamGreen | 0:616601bde9fb | 2019 | stack for this new client connection on port 80. |
AdamGreen | 0:616601bde9fb | 2020 | err is passed in from the lwIP stack to indicate if an error occurred. |
AdamGreen | 0:616601bde9fb | 2021 | |
AdamGreen | 0:616601bde9fb | 2022 | Returns: |
AdamGreen | 0:616601bde9fb | 2023 | ERR_MEM if we don't have enough memory for this connection and ERR_OK |
AdamGreen | 0:616601bde9fb | 2024 | otherwise. |
AdamGreen | 0:616601bde9fb | 2025 | */ |
AdamGreen | 0:616601bde9fb | 2026 | err_t CHTTPServer::HTTPAccept(void* pvArg, tcp_pcb* pNewPCB, err_t err) |
AdamGreen | 0:616601bde9fb | 2027 | { |
AdamGreen | 0:616601bde9fb | 2028 | CHTTPServer* pHTTPServer = (CHTTPServer*)pvArg; |
AdamGreen | 0:616601bde9fb | 2029 | CHTTPContext* pHTTPContext = NULL; |
AdamGreen | 0:616601bde9fb | 2030 | (void)err; |
AdamGreen | 0:616601bde9fb | 2031 | |
AdamGreen | 0:616601bde9fb | 2032 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 2033 | assert ( pvArg ); |
AdamGreen | 0:616601bde9fb | 2034 | assert ( pNewPCB ); |
AdamGreen | 0:616601bde9fb | 2035 | |
AdamGreen | 0:616601bde9fb | 2036 | // Let user know that we have just accepted a new connection. |
AdamGreen | 0:616601bde9fb | 2037 | TRACE("HTTP: Accepting client connection from: "); |
AdamGreen | 0:616601bde9fb | 2038 | NetworkPrintRemoteAddress(pNewPCB); |
AdamGreen | 0:616601bde9fb | 2039 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 2040 | |
AdamGreen | 0:616601bde9fb | 2041 | // Let the lwIP stack know that the HTTP application has successfuly |
AdamGreen | 0:616601bde9fb | 2042 | // received the client connection on the listening PCB. |
AdamGreen | 0:616601bde9fb | 2043 | tcp_accepted(pHTTPServer->m_pHTTPListenPCB); |
AdamGreen | 0:616601bde9fb | 2044 | |
AdamGreen | 0:616601bde9fb | 2045 | // UNDONE: Remove this new call and use a pool instead. |
AdamGreen | 0:616601bde9fb | 2046 | // Allocate memory to track context for this client connection and |
AdamGreen | 0:616601bde9fb | 2047 | // associate with the PCB. |
AdamGreen | 0:616601bde9fb | 2048 | pHTTPContext = new CHTTPContext(pHTTPServer, pNewPCB); |
AdamGreen | 0:616601bde9fb | 2049 | if (!pHTTPContext) |
AdamGreen | 0:616601bde9fb | 2050 | { |
AdamGreen | 0:616601bde9fb | 2051 | TRACE("HTTP: Failed to allocate client context for: "); |
AdamGreen | 0:616601bde9fb | 2052 | NetworkPrintRemoteAddress(pNewPCB); |
AdamGreen | 0:616601bde9fb | 2053 | TRACE("\r\n"); |
AdamGreen | 0:616601bde9fb | 2054 | |
AdamGreen | 0:616601bde9fb | 2055 | // lwIP will shutdown this connection when it get back a return value |
AdamGreen | 0:616601bde9fb | 2056 | // other than ERR_OK. |
AdamGreen | 0:616601bde9fb | 2057 | return ERR_MEM; |
AdamGreen | 0:616601bde9fb | 2058 | } |
AdamGreen | 0:616601bde9fb | 2059 | |
AdamGreen | 0:616601bde9fb | 2060 | return ERR_OK; |
AdamGreen | 0:616601bde9fb | 2061 | } |
AdamGreen | 0:616601bde9fb | 2062 | |
AdamGreen | 0:616601bde9fb | 2063 | |
AdamGreen | 0:616601bde9fb | 2064 | /* Removes the specified context from the linked list of waiting contexts. |
AdamGreen | 0:616601bde9fb | 2065 | |
AdamGreen | 0:616601bde9fb | 2066 | Parameters: |
AdamGreen | 0:616601bde9fb | 2067 | pHTTPContext is a pointer to the context object for the client connection |
AdamGreen | 0:616601bde9fb | 2068 | to be removed. |
AdamGreen | 0:616601bde9fb | 2069 | |
AdamGreen | 0:616601bde9fb | 2070 | Returns: |
AdamGreen | 0:616601bde9fb | 2071 | Nothing. |
AdamGreen | 0:616601bde9fb | 2072 | */ |
AdamGreen | 0:616601bde9fb | 2073 | void CHTTPServer::RemoveWaitingContext(CHTTPContext* pHTTPContext) |
AdamGreen | 0:616601bde9fb | 2074 | { |
AdamGreen | 0:616601bde9fb | 2075 | // Find this node in the waiting queue and remove it. |
AdamGreen | 0:616601bde9fb | 2076 | CHTTPContext* pPrev = NULL; |
AdamGreen | 0:616601bde9fb | 2077 | CHTTPContext* pCurr = m_pContextWaitingHead; |
AdamGreen | 0:616601bde9fb | 2078 | while(pCurr) |
AdamGreen | 0:616601bde9fb | 2079 | { |
AdamGreen | 0:616601bde9fb | 2080 | CHTTPContext* pNext = pCurr->GetNext(); |
AdamGreen | 0:616601bde9fb | 2081 | |
AdamGreen | 0:616601bde9fb | 2082 | if (pCurr == pHTTPContext) |
AdamGreen | 0:616601bde9fb | 2083 | { |
AdamGreen | 0:616601bde9fb | 2084 | // Found this context. Now remove it and exit loop. |
AdamGreen | 0:616601bde9fb | 2085 | pCurr->RemovedFromWaitingList(pPrev); |
AdamGreen | 0:616601bde9fb | 2086 | if (!pPrev) |
AdamGreen | 0:616601bde9fb | 2087 | { |
AdamGreen | 0:616601bde9fb | 2088 | // This node was at the head so update the head pointer. |
AdamGreen | 0:616601bde9fb | 2089 | m_pContextWaitingHead = pNext; |
AdamGreen | 0:616601bde9fb | 2090 | } |
AdamGreen | 0:616601bde9fb | 2091 | if (!pNext) |
AdamGreen | 0:616601bde9fb | 2092 | { |
AdamGreen | 0:616601bde9fb | 2093 | // This node was at the tail so reset the tail to previous node. |
AdamGreen | 0:616601bde9fb | 2094 | m_pContextWaitingTail = pPrev; |
AdamGreen | 0:616601bde9fb | 2095 | } |
AdamGreen | 0:616601bde9fb | 2096 | break; |
AdamGreen | 0:616601bde9fb | 2097 | } |
AdamGreen | 0:616601bde9fb | 2098 | pPrev = pCurr; |
AdamGreen | 0:616601bde9fb | 2099 | pCurr = pNext; |
AdamGreen | 0:616601bde9fb | 2100 | } |
AdamGreen | 0:616601bde9fb | 2101 | // We should always find this context in the list. |
AdamGreen | 0:616601bde9fb | 2102 | assert ( pCurr == pHTTPContext ); |
AdamGreen | 0:616601bde9fb | 2103 | } |
AdamGreen | 0:616601bde9fb | 2104 | |
AdamGreen | 0:616601bde9fb | 2105 | |
AdamGreen | 0:616601bde9fb | 2106 | /* Attempts to allocate a SBuffer object into which content can be placed for |
AdamGreen | 0:616601bde9fb | 2107 | sending back to the HTTP client. If it fails to find a free buffer in the |
AdamGreen | 0:616601bde9fb | 2108 | server's pool then it will add the client context to a queue and return |
AdamGreen | 0:616601bde9fb | 2109 | NULL. |
AdamGreen | 0:616601bde9fb | 2110 | |
AdamGreen | 0:616601bde9fb | 2111 | Parameters: |
AdamGreen | 0:616601bde9fb | 2112 | pHTTPContext is a pointer to the context asking for a buffer object. |
AdamGreen | 0:616601bde9fb | 2113 | |
AdamGreen | 0:616601bde9fb | 2114 | Returns: |
AdamGreen | 0:616601bde9fb | 2115 | A pointer to a free SBuffer object or NULL if none are free. |
AdamGreen | 0:616601bde9fb | 2116 | */ |
AdamGreen | 0:616601bde9fb | 2117 | SBuffer* CHTTPServer::AllocateBuffer(CHTTPContext* pHTTPContext) |
AdamGreen | 0:616601bde9fb | 2118 | { |
AdamGreen | 0:616601bde9fb | 2119 | CHTTPContext* pPrev = NULL; |
AdamGreen | 0:616601bde9fb | 2120 | |
AdamGreen | 0:616601bde9fb | 2121 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 2122 | assert ( pHTTPContext ); |
AdamGreen | 0:616601bde9fb | 2123 | |
AdamGreen | 0:616601bde9fb | 2124 | // Local variables. |
AdamGreen | 0:616601bde9fb | 2125 | size_t i; |
AdamGreen | 0:616601bde9fb | 2126 | |
AdamGreen | 0:616601bde9fb | 2127 | // Just return NULL if this context is already waiting for a buffer. |
AdamGreen | 0:616601bde9fb | 2128 | if (pHTTPContext->IsWaitingForBuffer()) |
AdamGreen | 0:616601bde9fb | 2129 | { |
AdamGreen | 0:616601bde9fb | 2130 | return NULL; |
AdamGreen | 0:616601bde9fb | 2131 | } |
AdamGreen | 0:616601bde9fb | 2132 | |
AdamGreen | 0:616601bde9fb | 2133 | // Search for a free buffer in the pool. |
AdamGreen | 0:616601bde9fb | 2134 | for (i = 0 ; i < ARRAYSIZE(m_BufferPool) ; i++) |
AdamGreen | 0:616601bde9fb | 2135 | { |
AdamGreen | 0:616601bde9fb | 2136 | // A buffer is free if it has no bytes left to write to the connection |
AdamGreen | 0:616601bde9fb | 2137 | // and the remote machine has acknowledged all previously written bytes |
AdamGreen | 0:616601bde9fb | 2138 | // from this buffer. |
AdamGreen | 0:616601bde9fb | 2139 | SBuffer* pBuffer = &(m_BufferPool[i]); |
AdamGreen | 0:616601bde9fb | 2140 | |
AdamGreen | 0:616601bde9fb | 2141 | if (0 == pBuffer->BytesToWrite && |
AdamGreen | 0:616601bde9fb | 2142 | 0 == pBuffer->UnacknowledgedBytes) |
AdamGreen | 0:616601bde9fb | 2143 | { |
AdamGreen | 0:616601bde9fb | 2144 | return pBuffer; |
AdamGreen | 0:616601bde9fb | 2145 | } |
AdamGreen | 0:616601bde9fb | 2146 | } |
AdamGreen | 0:616601bde9fb | 2147 | |
AdamGreen | 0:616601bde9fb | 2148 | // Get here if there wasn't a free buffer in the pool so add this context |
AdamGreen | 0:616601bde9fb | 2149 | // to the queue to be serviced later. |
AdamGreen | 0:616601bde9fb | 2150 | assert (!pHTTPContext->GetNext()); |
AdamGreen | 0:616601bde9fb | 2151 | if (m_pContextWaitingTail) |
AdamGreen | 0:616601bde9fb | 2152 | { |
AdamGreen | 0:616601bde9fb | 2153 | // Add to end of list. |
AdamGreen | 0:616601bde9fb | 2154 | pPrev = m_pContextWaitingTail; |
AdamGreen | 0:616601bde9fb | 2155 | m_pContextWaitingTail = pHTTPContext; |
AdamGreen | 0:616601bde9fb | 2156 | } |
AdamGreen | 0:616601bde9fb | 2157 | else |
AdamGreen | 0:616601bde9fb | 2158 | { |
AdamGreen | 0:616601bde9fb | 2159 | // Adding entry to empty queue. |
AdamGreen | 0:616601bde9fb | 2160 | assert (!m_pContextWaitingHead); |
AdamGreen | 0:616601bde9fb | 2161 | |
AdamGreen | 0:616601bde9fb | 2162 | m_pContextWaitingHead = pHTTPContext; |
AdamGreen | 0:616601bde9fb | 2163 | m_pContextWaitingTail = pHTTPContext; |
AdamGreen | 0:616601bde9fb | 2164 | } |
AdamGreen | 0:616601bde9fb | 2165 | pHTTPContext->AddToWaitingList(pPrev); |
AdamGreen | 0:616601bde9fb | 2166 | |
AdamGreen | 0:616601bde9fb | 2167 | // Let calling context know that it will have to wait until later for a |
AdamGreen | 0:616601bde9fb | 2168 | // buffer. |
AdamGreen | 0:616601bde9fb | 2169 | return NULL; |
AdamGreen | 0:616601bde9fb | 2170 | } |
AdamGreen | 0:616601bde9fb | 2171 | |
AdamGreen | 0:616601bde9fb | 2172 | |
AdamGreen | 0:616601bde9fb | 2173 | /* Frees a SBuffer object back into the server's pool. If there is a client |
AdamGreen | 0:616601bde9fb | 2174 | context waiting in the queue, then callback into the context and let |
AdamGreen | 0:616601bde9fb | 2175 | it use the buffer now that it is free. |
AdamGreen | 0:616601bde9fb | 2176 | |
AdamGreen | 0:616601bde9fb | 2177 | Parameters: |
AdamGreen | 0:616601bde9fb | 2178 | pBuffer is a pointer to the buffer to be freed. |
AdamGreen | 0:616601bde9fb | 2179 | |
AdamGreen | 0:616601bde9fb | 2180 | Returns: |
AdamGreen | 0:616601bde9fb | 2181 | Nothing. |
AdamGreen | 0:616601bde9fb | 2182 | */ |
AdamGreen | 0:616601bde9fb | 2183 | void CHTTPServer::FreeBuffer(SBuffer* pBuffer) |
AdamGreen | 0:616601bde9fb | 2184 | { |
AdamGreen | 0:616601bde9fb | 2185 | // Validate parameters. |
AdamGreen | 0:616601bde9fb | 2186 | assert ( pBuffer ); |
AdamGreen | 0:616601bde9fb | 2187 | |
AdamGreen | 0:616601bde9fb | 2188 | // The SBuffer shouldn't be in use when this routine is called. |
AdamGreen | 0:616601bde9fb | 2189 | assert ( 0 == pBuffer->BytesToWrite ); |
AdamGreen | 0:616601bde9fb | 2190 | assert ( 0 == pBuffer->UnacknowledgedBytes ); |
AdamGreen | 0:616601bde9fb | 2191 | |
AdamGreen | 0:616601bde9fb | 2192 | // See if there are other client connection waiting for a buffer to become |
AdamGreen | 0:616601bde9fb | 2193 | // free. If so, give it an apportunity to make use of this one. |
AdamGreen | 0:616601bde9fb | 2194 | if (m_pContextWaitingHead) |
AdamGreen | 0:616601bde9fb | 2195 | { |
AdamGreen | 0:616601bde9fb | 2196 | CHTTPContext* pHTTPContext = m_pContextWaitingHead; |
AdamGreen | 0:616601bde9fb | 2197 | |
AdamGreen | 0:616601bde9fb | 2198 | // Remove the context from the waiting list. |
AdamGreen | 0:616601bde9fb | 2199 | m_pContextWaitingHead = pHTTPContext->GetNext(); |
AdamGreen | 0:616601bde9fb | 2200 | if (!m_pContextWaitingHead) |
AdamGreen | 0:616601bde9fb | 2201 | { |
AdamGreen | 0:616601bde9fb | 2202 | // We just emptied the list so clear tail pointer as well. |
AdamGreen | 0:616601bde9fb | 2203 | assert ( m_pContextWaitingTail == pHTTPContext ); |
AdamGreen | 0:616601bde9fb | 2204 | |
AdamGreen | 0:616601bde9fb | 2205 | m_pContextWaitingTail = NULL; |
AdamGreen | 0:616601bde9fb | 2206 | } |
AdamGreen | 0:616601bde9fb | 2207 | |
AdamGreen | 0:616601bde9fb | 2208 | // Let context know that it is no longer in a list. |
AdamGreen | 0:616601bde9fb | 2209 | pHTTPContext->RemovedFromWaitingList(NULL); |
AdamGreen | 0:616601bde9fb | 2210 | pHTTPContext->BufferAvailable(); |
AdamGreen | 0:616601bde9fb | 2211 | } |
AdamGreen | 0:616601bde9fb | 2212 | } |
AdamGreen | 0:616601bde9fb | 2213 | |
AdamGreen | 0:616601bde9fb | 2214 | |
AdamGreen | 0:616601bde9fb | 2215 | /* Prints out the remote machine IP address and port number. |
AdamGreen | 0:616601bde9fb | 2216 | |
AdamGreen | 0:616601bde9fb | 2217 | Parameters: |
AdamGreen | 0:616601bde9fb | 2218 | pPCB is a pointer to the TCP protocol control block. |
AdamGreen | 0:616601bde9fb | 2219 | |
AdamGreen | 0:616601bde9fb | 2220 | Returns: |
AdamGreen | 0:616601bde9fb | 2221 | Nothing. |
AdamGreen | 0:616601bde9fb | 2222 | */ |
AdamGreen | 0:616601bde9fb | 2223 | static void NetworkPrintRemoteAddress(tcp_pcb* pPCB) |
AdamGreen | 0:616601bde9fb | 2224 | { |
AdamGreen | 0:616601bde9fb | 2225 | // Validate paramters. |
AdamGreen | 0:616601bde9fb | 2226 | assert ( pPCB ); |
AdamGreen | 0:616601bde9fb | 2227 | |
AdamGreen | 0:616601bde9fb | 2228 | if (HTTP_TRACE) |
AdamGreen | 0:616601bde9fb | 2229 | { |
AdamGreen | 0:616601bde9fb | 2230 | SNetwork_PrintAddress(&pPCB->remote_ip); |
AdamGreen | 0:616601bde9fb | 2231 | printf(":%u", pPCB->remote_port); |
AdamGreen | 0:616601bde9fb | 2232 | } |
AdamGreen | 0:616601bde9fb | 2233 | } |