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

Last change on this file since 611 was 611, checked in by sjboddie, 25 years ago

initial commit of windows server code

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