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

Last change on this file since 3056 was 3056, checked in by sjboddie, 22 years ago

Commented out recent change to make socket read non-blocking. This
change didn't appear to have the desired effect (certainly on Windows 98
the problems remained) and relied on wsock32.lib being linked at compile
time. Including wsock32 might be ok but it needs some testing to be sure.
That is, how will it react with restricted (billsock) version, particularly
on machines that don't have networking installed? For now it seems safer
to live with the bug we know.

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