source: trunk/gsdl/src/w32server/httpreq.cpp@ 2280

Last change on this file since 2280 was 2280, checked in by sjboddie, 23 years ago

In the process of making some changes to the old fnord webserver code.
Note that this version is BROKEN, I just committed it so I could continue
the work I've been doing on my home PC at work.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 16.7 KB
Line 
1/*
2Copyright (C) 1996
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18The author can be contacted via Email at [email protected]
19*/
20#include "text_t.h"
21#include <windows.h>
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <memory.h>
26#pragma hdrstop
27#include "parse.h"
28#include "netio.h"
29#include "settings.h"
30#include "httpreq.h" //Had to put myself here because of the global types...
31#include "httpsrv.h"
32#include "httpsend.h"
33#include "cgiwrapper.h"
34
35/*
36Implementation Notes:
37
38HTTP field names, method and version strings are converted to upper case
39right after being read from the client in order to allow case insensitive
40string comparisons to be done on them. Since these fields are worked with a
41lot, this should help performance.
42*/
43
44//Private Data and declarations
45#define IO_BUFFER_SIZE 16384 //16K IO Buffer
46#define MAX_HTTP_LINE_LEN 1024 //Max length of line in a header of 1024
47#define MAX_HTTP_FIELD_NAME_LEN 128 //Max length of name field in line
48#define MAX_HTTP_FIELD_LEN 1024 //Max length of data in line
49
50//Private Function Declarations with Return Contstants
51
52/*
53Function Name: DispatchRequest
54Purpose: Manages having the request parsed, then sent to the right function
55 to send a response or handle an error.
56Parameters:
57 ClientSocket - Socket the client is on
58 ClientSockAddr - Address of client
59 AddrLen - Length of address of client
60 IOBuffer - Pointer to buffer allocated for IO operations
61 ThreadNum - Number of thread that called this function for debugging purposes
62Notes: I'm still playing with the keep alive support. I commented out
63 the stuff for giving a client a timeout because I was unable to detect
64 disconnects.
65More Notes: Not sure if this organization will allow me to easily add support
66 for ISAPI filter DLLs.
67*/
68void DispatchRequest(SOCKET ClientSocket, SOCKADDR_IN ClientSockAddr, int AddrLen, BYTE *IOBuffer);
69
70/*
71Function Name: Get HTTP Headers
72Purpose: Manages having the request parsed, then sent to the right function
73 to send a response or handle an error.
74Parameters:
75 RequestInfo - Request information structure (see httpreq.h)
76 RequestFields - HTTP request fields structure (see httpreq.h)
77Returns: GH_ERROR on error (diconnect, bad data, Windows in a bad mood, etc.)
78 GH_UNKNOWN_VERSION if the version number is not HTTP/0.9 or HTTP/1.x
79 GH_SIMPLE_REQUEST on a properly formated HTTP/0.9 request
80 GH_10_REQUEST on a properly formated HTTP/1.x request
81*/
82int GetHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
83#define GH_ERROR -1
84#define GH_UNKNOWN_VERSION 0
85#define GH_SIMPLE_REQUEST 1
86#define GH_10_REQUEST 2
87
88/*
89Function Name: Clean Up HTTP Headers
90Purpose: Cleans up memory dynamicly allocated for headers
91Parameters:
92 RequestInfo - Request information structure (see httpreq.h)
93 RequestFields - HTTP request fields structure (see httpreq.h)
94Returns: Nothing
95*/
96void CleanUpHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
97
98/*
99Function Name: Split Query
100Purpose: Splits the file and query part of a URI. In other words, it
101 puts the parts before and after the "?" in differnet strings.
102Parameters:
103 URIStr - The requested URI
104 FileStr - String to contain the name of the path + file part of the URI
105 QueryStr - String to contain the query part of the URI
106Returns: TRUE if there is a query, else FALSE
107*/
108BOOL SplitQuery(char *URIStr, char *FileStr, char *QueryStr, int ThreadNum);
109
110/*
111Function Name: Get File
112Purpose: Attempts to find a given file, including looking for index.html.
113 Updates the given URI string so it points to the true document location
114Parameters:
115 FilePath - Path of file, may be modified to best reflect the retrived file
116 or directory
117 URIStr - URI string, minus the query
118Returns: GF_ERROR on error
119 GF_FILE_FOUND on success
120 GF_INDEX_FOUND if file is a directory with an index.html file in it
121 GF_DIRECTORY if file is a directory
122 GF_FILE_NOT_FOUND if file was found
123*/
124
125/*
126Function Name: Process Simple Request
127Purpose: Sends a reply to a HTTP 0.9 "simple" request
128Parameters:
129 ClientSocket - Socket the client is on
130 RequestInfo - Structure storing the parsed headers
131 IOBuffer - Pointer to buffer allocated for IO operations
132 TheadNum - Number of calling thread for debugging
133Notes: I should really test this and see if it works...
134*/
135void ProcessSimpleRequest(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
136
137//Public Functions
138/******************************************************************************/
139void RequestThread(RequestThreadMessageT *Parameters) {
140 SOCKADDR_IN ClientSockAddr;
141 SOCKET ClientSocket;
142 int AddrLen;
143 //Allocate an IO buffer for this thread
144 BYTE *IOBuffer = new BYTE[IO_BUFFER_SIZE];
145
146 //Get the parameters for the request
147 ClientSocket = Parameters->ClientSocket;
148 ClientSockAddr = Parameters->ClientSockAddr;
149 AddrLen = Parameters->AddrLen;
150 DispatchRequest(ClientSocket, ClientSockAddr, AddrLen, IOBuffer);
151}
152/******************************************************************************/
153
154//Private Functions
155
156/******************************************************************************/
157void DispatchRequest(SOCKET ClientSocket, SOCKADDR_IN ClientSockAddr, int AddrLen, BYTE *IOBuffer) {
158 RequestInfoT RequestInfo;
159 RequestFieldsT RequestFields;
160
161 // TrayAddConnection();
162
163 //Setup the RequestInfo structure
164 memset(&RequestInfo, 0, sizeof(RequestInfoT));
165 RequestInfo.ThreadNum = 0;
166 RequestInfo.IOBuffer = IOBuffer;
167 RequestInfo.IOBufferSize = IO_BUFFER_SIZE;
168 RequestInfo.ClientSocket = ClientSocket;
169 RequestInfo.ClientSockAddr = ClientSockAddr;
170 RequestInfo.AddrLen = AddrLen;
171 RequestInfo.KeepAlive = FALSE;
172
173 int GetHeadersResult;
174 do {
175 //Get Headers
176 GetHeadersResult = GetHTTPHeaders(RequestInfo, RequestFields);
177 //Figure out what version we're dealing with and deal with it
178 switch (GetHeadersResult) {
179 case GH_SIMPLE_REQUEST :
180 SendHTTPError(400, "HTTP Request not supported", "Only 1.x requests supported", RequestInfo, RequestFields);
181 // TrayIncNumServed();
182 break;
183 case GH_10_REQUEST :
184 ExamineURIStr(RequestFields.URIStr,&RequestInfo,&RequestFields);
185 // TrayIncNumServed();
186 break;
187 case GH_UNKNOWN_VERSION :
188 SendHTTPError(400, "HTTP Version not supported", "Only 1.x requests supported", RequestInfo, RequestFields);
189 // TrayIncNumServed();
190 break;
191 case GH_ERROR:
192 //Disconnect
193 RequestInfo.KeepAlive = FALSE;
194 break;
195 }
196 CleanUpHTTPHeaders(RequestInfo, RequestFields);
197 } while (0/*RequestInfo.KeepAlive == TRUE*/);
198 //Close connection
199 CloseSocket(RequestInfo.ClientSocket);
200 // TrayRemoveConnection();
201}
202
203/******************************************************************************/
204int GetHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields) {
205 //Parsing and IO buffers
206 char CurLine[NETIO_MAX_LINE];
207 char NextLine[NETIO_MAX_LINE];
208 char FieldNameStr[MAX_HTTP_FIELD_NAME_LEN];
209 char FieldValStr[MAX_HTTP_FIELD_LEN];
210
211 //Parsing and IO working vars
212 int ReadBufferIndex;
213 int DataInBuffer;
214 int Start;
215 int End;
216 int Len;
217
218 //Clear all the fields
219 memset(&RequestFields, 0, sizeof(RequestFieldsT));
220
221 ReadBufferIndex = 0;
222 DataInBuffer = 0;
223
224 //Get First Line
225 if (GetLine(CurLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
226 RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
227 RequestInfo.ThreadNum) != 0) return GH_ERROR;
228 do {//Get Next Line, append it if the first charactor is space
229 if(GetLine(NextLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
230 RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
231 RequestInfo.ThreadNum) != 0) return GH_ERROR;
232 if ((NextLine[0] == ' ') || (NextLine[0] == '\t'))
233 strcat(CurLine, NextLine);
234 } while ((NextLine[0] == ' ') || (NextLine[0] == '\t'));
235 //Method String (first word)
236 Start = 0;
237 GetWord(RequestFields.MethodStr, CurLine, Start, End);
238 CharUpper(RequestFields.MethodStr);
239
240 //Version String (last word)
241 GetLastWord(RequestFields.VersionStr, CurLine, Start);
242 CharUpper(RequestFields.VersionStr);
243
244 if (strncmp(RequestFields.VersionStr, "HTTP/", 5) != 0) {
245 //No version, assume simple request
246 //part after method is URI
247 for (int i = 0; i < strlen(CurLine); i++) {
248 RequestFields.URIStr.push_back(CurLine[i]);
249 }
250 return GH_SIMPLE_REQUEST;
251 }
252
253 //URI String (in between End of first and Start of last)
254 //<Method> <WhiteSpace> <URI> <WhiteSpace> <Version> <CRLF>
255 // End^ Start^
256 text_t spacebuffer;
257 for (int i = End; i < Start; i++) {
258 // do this to remove trailing space
259 if (CurLine[i] == ' ') {
260 spacebuffer.push_back(' ');
261 } else {
262 if (!spacebuffer.empty()) {
263 RequestFields.URIStr += spacebuffer;
264 spacebuffer.clear();
265 }
266 RequestFields.URIStr.push_back(CurLine[i]);
267 }
268 }
269
270 //Only accept requests from HTTP/0.9 or HTTP/1.X clients, we'll
271 //assume that anything else will require an upgrade or patch
272 if (strncmp(RequestFields.VersionStr, "HTTP/1.", 7) != 0)
273 return GH_UNKNOWN_VERSION;
274
275 //Get the rest of the lines
276
277 strcpy(CurLine, NextLine);
278
279 while (CurLine[0] != 0) {//Blank Line, we're done
280 do {//Get Next Line, append it if the first charactor is space
281 if (GetLine(NextLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
282 RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
283 RequestInfo.ThreadNum) != 0)
284 return GH_ERROR;
285 if ((NextLine[0] == ' ') || (NextLine[0] == '\t'))
286 strcat(CurLine, NextLine);
287 } while ((NextLine[0] == ' ') || (NextLine[0] == '\t'));
288
289 Start = 0;
290 GetWord(FieldNameStr, CurLine, Start, End);
291 CharUpper(FieldNameStr);
292
293 Len = strlen(CurLine) - End;
294 memcpy(FieldValStr, CurLine + End, Len);
295 FieldValStr[Len] = 0;
296
297 //Process it
298 //In order of expected commonality
299 //All constants are in canonized, thus in upper case and case sensitive
300 //comparisons are used
301 //--Just About Always--
302 if (strcmp("ACCEPT:", FieldNameStr) == 0) {
303 if (RequestFields.AcceptStr[0] == '\0') {
304 strncpy(RequestFields.AcceptStr, FieldValStr, ReqAcceptStrLen - 1);
305 // if (Len >= ReqAcceptStrLen)
306 // if (log_debug) log_message("Accept field truncated");
307 }
308 else {
309 //Append it with a comma
310 int AcceptStrLen = strlen(RequestFields.AcceptStr);
311 if ((ReqAcceptStrLen - AcceptStrLen) >= 10) {
312 strncat(RequestFields.AcceptStr, ", ", ReqAcceptStrLen - AcceptStrLen - 1);
313 strncat(RequestFields.AcceptStr, FieldValStr, ReqAcceptStrLen - AcceptStrLen - 3);
314 }
315 }
316 }
317 else if (strcmp("DATE:", FieldNameStr) == 0) {
318 strncpy(RequestFields.DateStr, FieldValStr, ReqDateStrLen - 1);
319 // if (Len >= ReqDateStrLen) LogError("Date field truncated");
320 }
321 else if (strcmp("USER-AGENT:", FieldNameStr) == 0) {
322 strncpy(RequestFields.UserAgentStr, FieldValStr, ReqUserAgentStrLen - 1);
323 // if (Len >= ReqUserAgentStrLen) {
324 // LogError("User Agent field truncated, value follows");
325 // LogError(RequestFields.UserAgentStr);
326 // }
327 }
328 else if (strcmp("CONNECTION:", FieldNameStr) == 0) {
329 strncpy(RequestFields.ConnectionStr, FieldValStr, ReqConnectionStrLen - 1);
330 // if (Len >= ReqConnectionStrLen) LogError("Connection field truncated");
331 }
332 //--Sometimes--
333 else if (strcmp("ACCEPT-LANGUAGE:", FieldNameStr) == 0) {
334 strncpy(RequestFields.AcceptLangStr, FieldValStr, ReqAcceptLangStrLen - 1);
335 // if (Len >= ReqAcceptLangStrLen) LogError("Accept-Language field truncated");
336 }
337 else if (strcmp("REFERER:", FieldNameStr) == 0) {
338 strncpy(RequestFields.RefererStr, FieldValStr, ReqRefererStrLen - 1);
339 // if (Len >= ReqRefererStrLen) LogError("Referer field truncated");
340 }
341 else if (strcmp("IF-MODIFIED-SINCE:", FieldNameStr) == 0) {
342 strncpy(RequestFields.IfModSinceStr, FieldValStr, ReqIfModSinceStrLen - 1);
343 // if (Len >= ReqIfModSinceStrLen) LogError("If Modified Since field truncated");
344 }
345 //--Uncommon--
346 else if (strcmp("FROM:", FieldNameStr) == 0) {
347 strncpy(RequestFields.FromStr, FieldValStr, ReqFromStrLen - 1);
348 // if (Len >= ReqFromStrLen) LogError("From field truncated");
349 }
350 else if (strcmp("MIME-VERSION:", FieldNameStr) == 0) {
351 strncpy(RequestFields.MIMEVerStr, FieldValStr, ReqMIMEVerStrLen - 1);
352 // if (Len >= ReqMIMEVerStrLen) LogError("MIME Version field truncated");
353 }
354 else if (strcmp("PRAGMA:", FieldNameStr) == 0) {
355 strncpy(RequestFields.PragmaStr, FieldValStr, ReqPragmaStrLen - 1);
356 // if (Len >= ReqPragmaStrLen) LogError("Pragma field truncated");
357 }
358 //--Special case--
359 else if (strcmp("AUTHORIZATION:", FieldNameStr) == 0) {
360 strncpy(RequestFields.AuthorizationStr, FieldValStr, ReqAuthorizationStrLen - 1);
361 // if (Len >= ReqAuthorizationStrLen) LogError("Authorization field truncated");
362 }
363 else if (strcmp("CONTENT-LENGTH:", FieldNameStr) == 0) {
364 strncpy(RequestFields.ContentLengthStr, FieldValStr, ReqContentLengthStrLen - 1);
365 // if (Len >= ReqContentLengthStrLen) LogError("Content Length field truncated");
366 }
367 else if (strcmp("CONTENT-TYPE:", FieldNameStr) == 0) {
368 strncpy(RequestFields.ContentTypeStr, FieldValStr, ReqContentTypeStrLen - 1);
369 // if (Len >= ReqContentTypeStrLen) LogError("Content Type field truncated");
370 }
371 else if (strcmp("CONTENT-ENCODING:", FieldNameStr) == 0) {
372 strncpy(RequestFields.ContentEncodingStr, FieldValStr, ReqContentEncodingStrLen - 1);
373 // if (Len >= ReqContentEncodingStrLen) LogError("Content Encoding field truncated");
374 }
375 else {
376 //Add it to the other headers
377 int VarLen = strlen(FieldNameStr);
378 if (FieldNameStr[VarLen - 1] == ':') {
379 //Remove the colon
380 FieldNameStr[VarLen - 1] = '\0';
381 VarLen--;
382 }
383 RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var = new char[VarLen + 1];
384 RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val = new char[Len + 1];
385 strcpy(RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var, FieldNameStr);
386 strcpy(RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val, FieldValStr);
387 RequestFields.NumOtherHeaders++;
388 }
389 strcpy(CurLine, NextLine);
390 }
391
392
393 if (RequestFields.ContentLengthStr[0] != 0) { //Do we have attached data?
394 unsigned int NumRecv;
395
396 RequestFields.ContentLength = atol(RequestFields.ContentLengthStr);
397 if (RequestFields.ContentLength > 0) {
398 // ThreadDebugMessage(RequestInfo.ThreadNum, "Getting content");
399
400 //Allocate memory
401 RequestFields.Content = new BYTE[RequestFields.ContentLength];
402
403 //Get rest of data from get lines
404 NumRecv = DataInBuffer - ReadBufferIndex;
405
406 if (NumRecv >RequestFields.ContentLength) {
407 //Overflow, only read what they said they'd send
408 NumRecv = RequestFields.ContentLength;
409 }
410 memcpy(RequestFields.Content, RequestInfo.IOBuffer + ReadBufferIndex,
411 NumRecv);
412
413 while (NumRecv < RequestFields.ContentLength) {
414 NumRecv = GetData(RequestInfo.ClientSocket,
415 RequestFields.Content + NumRecv,
416 RequestFields.ContentLength - NumRecv,
417 RequestInfo.ThreadNum);
418 if (NumRecv < 0) return GH_ERROR;
419 }
420 }
421 else {
422 RequestFields.Content = NULL;
423 RequestFields.ContentLength = 0;
424 }
425 }
426 else {
427 RequestFields.Content = NULL;
428 RequestFields.ContentLength = 0;
429 }
430
431 return GH_10_REQUEST;
432}
433
434/******************************************************************************/
435void CleanUpHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields) {
436 //Clean up memory allocated for the Content
437 if (RequestFields.Content != NULL)
438 delete[] RequestFields.Content;
439 while (RequestFields.NumOtherHeaders > 0) {
440 RequestFields.NumOtherHeaders--;
441 delete[] RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var;
442 delete[] RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val;
443 }
444
445 // clean up memory allocated for the IOBuffer
446 if (RequestInfo.IOBuffer != NULL) {
447 delete[] RequestInfo.IOBuffer;
448 RequestInfo.IOBuffer = NULL;
449 }
450}
Note: See TracBrowser for help on using the repository browser.