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