source: branches/New_Config_Format-branch/gsdl/src/w32server/httpreq.cpp@ 1279

Last change on this file since 1279 was 1279, checked in by sjboddie, 24 years ago

merged changes to trunk into New_Config_Format branch

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 16.5 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 memcpy(RequestFields.URIStr, CurLine + End, strlen(CurLine) + 1 - End);
248 return GH_SIMPLE_REQUEST;
249 }
250
251 //URI String (in between End of first and Start of last)
252 //<Method> <WhiteSpace> <URI> <WhiteSpace> <Version> <CRLF>
253 // End^ Start^
254 int URIStrLen = Start - End;;
255 if (URIStrLen > ReqURIStrLen-1) URIStrLen = ReqURIStrLen - 1;
256 memcpy(RequestFields.URIStr, CurLine + End, URIStrLen);
257 RequestFields.URIStr[URIStrLen] = 0;
258 TrimRight(RequestFields.URIStr); //Remove trailing space
259
260 //Only accept requests from HTTP/0.9 or HTTP/1.X clients, we'll
261 //assume that anything else will require an upgrade or patch
262 if (strncmp(RequestFields.VersionStr, "HTTP/1.", 7) != 0)
263 return GH_UNKNOWN_VERSION;
264
265 //Get the rest of the lines
266
267 strcpy(CurLine, NextLine);
268
269 while (CurLine[0] != 0) {//Blank Line, we're done
270 do {//Get Next Line, append it if the first charactor is space
271 if (GetLine(NextLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
272 RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
273 RequestInfo.ThreadNum) != 0)
274 return GH_ERROR;
275 if ((NextLine[0] == ' ') || (NextLine[0] == '\t'))
276 strcat(CurLine, NextLine);
277 } while ((NextLine[0] == ' ') || (NextLine[0] == '\t'));
278
279 Start = 0;
280 GetWord(FieldNameStr, CurLine, Start, End);
281 CharUpper(FieldNameStr);
282
283 Len = strlen(CurLine) - End;
284 memcpy(FieldValStr, CurLine + End, Len);
285 FieldValStr[Len] = 0;
286
287 //Process it
288 //In order of expected commonality
289 //All constants are in canonized, thus in upper case and case sensitive
290 //comparisons are used
291 //--Just About Always--
292 if (strcmp("ACCEPT:", FieldNameStr) == 0) {
293 if (RequestFields.AcceptStr[0] == '\0') {
294 strncpy(RequestFields.AcceptStr, FieldValStr, ReqAcceptStrLen - 1);
295 // if (Len >= ReqAcceptStrLen)
296 // if (log_debug) log_message("Accept field truncated");
297 }
298 else {
299 //Append it with a comma
300 int AcceptStrLen = strlen(RequestFields.AcceptStr);
301 if ((ReqAcceptStrLen - AcceptStrLen) >= 10) {
302 strncat(RequestFields.AcceptStr, ", ", ReqAcceptStrLen - AcceptStrLen - 1);
303 strncat(RequestFields.AcceptStr, FieldValStr, ReqAcceptStrLen - AcceptStrLen - 3);
304 }
305 }
306 }
307 else if (strcmp("DATE:", FieldNameStr) == 0) {
308 strncpy(RequestFields.DateStr, FieldValStr, ReqDateStrLen - 1);
309 // if (Len >= ReqDateStrLen) LogError("Date field truncated");
310 }
311 else if (strcmp("USER-AGENT:", FieldNameStr) == 0) {
312 strncpy(RequestFields.UserAgentStr, FieldValStr, ReqUserAgentStrLen - 1);
313 // if (Len >= ReqUserAgentStrLen) {
314 // LogError("User Agent field truncated, value follows");
315 // LogError(RequestFields.UserAgentStr);
316 // }
317 }
318 else if (strcmp("CONNECTION:", FieldNameStr) == 0) {
319 strncpy(RequestFields.ConnectionStr, FieldValStr, ReqConnectionStrLen - 1);
320 // if (Len >= ReqConnectionStrLen) LogError("Connection field truncated");
321 }
322 //--Sometimes--
323 else if (strcmp("ACCEPT-LANGUAGE:", FieldNameStr) == 0) {
324 strncpy(RequestFields.AcceptLangStr, FieldValStr, ReqAcceptLangStrLen - 1);
325 // if (Len >= ReqAcceptLangStrLen) LogError("Accept-Language field truncated");
326 }
327 else if (strcmp("REFERER:", FieldNameStr) == 0) {
328 strncpy(RequestFields.RefererStr, FieldValStr, ReqRefererStrLen - 1);
329 // if (Len >= ReqRefererStrLen) LogError("Referer field truncated");
330 }
331 else if (strcmp("IF-MODIFIED-SINCE:", FieldNameStr) == 0) {
332 strncpy(RequestFields.IfModSinceStr, FieldValStr, ReqIfModSinceStrLen - 1);
333 // if (Len >= ReqIfModSinceStrLen) LogError("If Modified Since field truncated");
334 }
335 //--Uncommon--
336 else if (strcmp("FROM:", FieldNameStr) == 0) {
337 strncpy(RequestFields.FromStr, FieldValStr, ReqFromStrLen - 1);
338 // if (Len >= ReqFromStrLen) LogError("From field truncated");
339 }
340 else if (strcmp("MIME-VERSION:", FieldNameStr) == 0) {
341 strncpy(RequestFields.MIMEVerStr, FieldValStr, ReqMIMEVerStrLen - 1);
342 // if (Len >= ReqMIMEVerStrLen) LogError("MIME Version field truncated");
343 }
344 else if (strcmp("PRAGMA:", FieldNameStr) == 0) {
345 strncpy(RequestFields.PragmaStr, FieldValStr, ReqPragmaStrLen - 1);
346 // if (Len >= ReqPragmaStrLen) LogError("Pragma field truncated");
347 }
348 //--Special case--
349 else if (strcmp("AUTHORIZATION:", FieldNameStr) == 0) {
350 strncpy(RequestFields.AuthorizationStr, FieldValStr, ReqAuthorizationStrLen - 1);
351 // if (Len >= ReqAuthorizationStrLen) LogError("Authorization field truncated");
352 }
353 else if (strcmp("CONTENT-LENGTH:", FieldNameStr) == 0) {
354 strncpy(RequestFields.ContentLengthStr, FieldValStr, ReqContentLengthStrLen - 1);
355 // if (Len >= ReqContentLengthStrLen) LogError("Content Length field truncated");
356 }
357 else if (strcmp("CONTENT-TYPE:", FieldNameStr) == 0) {
358 strncpy(RequestFields.ContentTypeStr, FieldValStr, ReqContentTypeStrLen - 1);
359 // if (Len >= ReqContentTypeStrLen) LogError("Content Type field truncated");
360 }
361 else if (strcmp("CONTENT-ENCODING:", FieldNameStr) == 0) {
362 strncpy(RequestFields.ContentEncodingStr, FieldValStr, ReqContentEncodingStrLen - 1);
363 // if (Len >= ReqContentEncodingStrLen) LogError("Content Encoding field truncated");
364 }
365 else {
366 //Add it to the other headers
367 int VarLen = strlen(FieldNameStr);
368 if (FieldNameStr[VarLen - 1] == ':') {
369 //Remove the colon
370 FieldNameStr[VarLen - 1] = '\0';
371 VarLen--;
372 }
373 RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var = new char[VarLen + 1];
374 RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val = new char[Len + 1];
375 strcpy(RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var, FieldNameStr);
376 strcpy(RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val, FieldValStr);
377 RequestFields.NumOtherHeaders++;
378 }
379 strcpy(CurLine, NextLine);
380 }
381
382
383 if (RequestFields.ContentLengthStr[0] != 0) { //Do we have attached data?
384 unsigned int NumRecv;
385
386 RequestFields.ContentLength = atol(RequestFields.ContentLengthStr);
387 if (RequestFields.ContentLength > 0) {
388 // ThreadDebugMessage(RequestInfo.ThreadNum, "Getting content");
389
390 //Allocate memory
391 RequestFields.Content = new BYTE[RequestFields.ContentLength];
392
393 //Get rest of data from get lines
394 NumRecv = DataInBuffer - ReadBufferIndex;
395
396 if (NumRecv >RequestFields.ContentLength) {
397 //Overflow, only read what they said they'd send
398 NumRecv = RequestFields.ContentLength;
399 }
400 memcpy(RequestFields.Content, RequestInfo.IOBuffer + ReadBufferIndex,
401 NumRecv);
402
403 while (NumRecv < RequestFields.ContentLength) {
404 NumRecv = GetData(RequestInfo.ClientSocket,
405 RequestFields.Content + NumRecv,
406 RequestFields.ContentLength - NumRecv,
407 RequestInfo.ThreadNum);
408 if (NumRecv < 0) return GH_ERROR;
409 }
410 }
411 else {
412 RequestFields.Content = NULL;
413 RequestFields.ContentLength = 0;
414 }
415 }
416 else {
417 RequestFields.Content = NULL;
418 RequestFields.ContentLength = 0;
419 }
420
421 return GH_10_REQUEST;
422}
423
424/******************************************************************************/
425void CleanUpHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields) {
426 //Clean up memory allocated for the Content
427 if (RequestFields.Content != NULL)
428 delete[] RequestFields.Content;
429 while (RequestFields.NumOtherHeaders > 0) {
430 RequestFields.NumOtherHeaders--;
431 delete[] RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var;
432 delete[] RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val;
433 }
434
435 // clean up memory allocated for the IOBuffer
436 if (RequestInfo.IOBuffer != NULL) {
437 delete[] RequestInfo.IOBuffer;
438 RequestInfo.IOBuffer = NULL;
439 }
440}
Note: See TracBrowser for help on using the repository browser.