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

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

Fixed a bug in the old fnord code that was causing problems if POST
data was longer than a certain buffer size. While playing with this
I also discovered that POST data fails when using the crippled billsock
version - not sure how to fix that yet.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 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_ERROR -1
93#define GH_UNKNOWN_VERSION 0
94#define GH_SIMPLE_REQUEST 1
95#define GH_10_REQUEST 2
96
97/*
98Function Name: Clean Up HTTP Headers
99Purpose: Cleans up memory dynamicly allocated for headers
100Parameters:
101 RequestInfo - Request information structure (see httpreq.h)
102 RequestFields - HTTP request fields structure (see httpreq.h)
103Returns: Nothing
104*/
105void CleanUpHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
106
107/*
108Function Name: Split Query
109Purpose: Splits the file and query part of a URI. In other words, it
110 puts the parts before and after the "?" in differnet strings.
111Parameters:
112 URIStr - The requested URI
113 FileStr - String to contain the name of the path + file part of the URI
114 QueryStr - String to contain the query part of the URI
115Returns: TRUE if there is a query, else FALSE
116*/
117BOOL SplitQuery(char *URIStr, char *FileStr, char *QueryStr, int ThreadNum);
118
119/*
120Function Name: Get File
121Purpose: Attempts to find a given file, including looking for index.html.
122 Updates the given URI string so it points to the true document location
123Parameters:
124 FilePath - Path of file, may be modified to best reflect the retrived file
125 or directory
126 URIStr - URI string, minus the query
127Returns: GF_ERROR on error
128 GF_FILE_FOUND on success
129 GF_INDEX_FOUND if file is a directory with an index.html file in it
130 GF_DIRECTORY if file is a directory
131 GF_FILE_NOT_FOUND if file was found
132*/
133
134/*
135Function Name: Process Simple Request
136Purpose: Sends a reply to a HTTP 0.9 "simple" request
137Parameters:
138 ClientSocket - Socket the client is on
139 RequestInfo - Structure storing the parsed headers
140 IOBuffer - Pointer to buffer allocated for IO operations
141 TheadNum - Number of calling thread for debugging
142Notes: I should really test this and see if it works...
143*/
144void ProcessSimpleRequest(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields);
145
146//Public Functions
147/******************************************************************************/
148void RequestThread(RequestThreadMessageT *Parameters) {
149 SOCKADDR_IN ClientSockAddr;
150 SOCKET ClientSocket;
151 int AddrLen;
152 //Allocate an IO buffer for this thread
153 BYTE *IOBuffer = new BYTE[IO_BUFFER_SIZE];
154
155 //Get the parameters for the request
156 ClientSocket = Parameters->ClientSocket;
157 ClientSockAddr = Parameters->ClientSockAddr;
158 AddrLen = Parameters->AddrLen;
159 DispatchRequest(ClientSocket, ClientSockAddr, AddrLen, IOBuffer);
160}
161/******************************************************************************/
162
163//Private Functions
164
165/******************************************************************************/
166void DispatchRequest(SOCKET ClientSocket, SOCKADDR_IN ClientSockAddr, int AddrLen, BYTE *IOBuffer) {
167 RequestInfoT RequestInfo;
168 RequestFieldsT RequestFields;
169
170 // TrayAddConnection();
171
172 //Setup the RequestInfo structure
173 memset(&RequestInfo, 0, sizeof(RequestInfoT));
174 RequestInfo.ThreadNum = 0;
175 RequestInfo.IOBuffer = IOBuffer;
176 RequestInfo.IOBufferSize = IO_BUFFER_SIZE;
177 RequestInfo.ClientSocket = ClientSocket;
178 RequestInfo.ClientSockAddr = ClientSockAddr;
179 RequestInfo.AddrLen = AddrLen;
180 RequestInfo.KeepAlive = FALSE;
181
182 int GetHeadersResult;
183 do {
184 //Get Headers
185 GetHeadersResult = GetHTTPHeaders(RequestInfo, RequestFields);
186
187 //Figure out what version we're dealing with and deal with it
188 switch (GetHeadersResult) {
189 case GH_SIMPLE_REQUEST :
190 SendHTTPError(400, "HTTP Request not supported", "Only 1.x requests supported", RequestInfo, RequestFields);
191 // TrayIncNumServed();
192 break;
193 case GH_10_REQUEST :
194 ExamineURIStr(RequestFields.URIStr,&RequestInfo,&RequestFields);
195 // TrayIncNumServed();
196 break;
197 case GH_UNKNOWN_VERSION :
198 SendHTTPError(400, "HTTP Version not supported", "Only 1.x requests supported", RequestInfo, RequestFields);
199 // TrayIncNumServed();
200 break;
201 case GH_ERROR:
202 //Disconnect
203 RequestInfo.KeepAlive = FALSE;
204 break;
205 }
206 CleanUpHTTPHeaders(RequestInfo, RequestFields);
207 } while (0/*RequestInfo.KeepAlive == TRUE*/);
208 //Close connection
209 CloseSocket(RequestInfo.ClientSocket);
210 // TrayRemoveConnection();
211}
212
213/******************************************************************************/
214int GetHTTPHeaders(RequestInfoT &RequestInfo, RequestFieldsT &RequestFields) {
215 //Parsing and IO buffers
216 char CurLine[NETIO_MAX_LINE];
217 char NextLine[NETIO_MAX_LINE];
218 char FieldNameStr[MAX_HTTP_FIELD_NAME_LEN];
219 char FieldValStr[MAX_HTTP_FIELD_LEN];
220
221 //Parsing and IO working vars
222 int ReadBufferIndex;
223 int DataInBuffer;
224 int Start;
225 int End;
226 int Len;
227
228 //Clear all the fields
229 memset(&RequestFields, 0, sizeof(RequestFieldsT));
230
231 ReadBufferIndex = 0;
232 DataInBuffer = 0;
233
234 //Get First Line
235 if (GetLine(CurLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
236 RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
237 RequestInfo.ThreadNum) != 0) return GH_ERROR;
238 do {//Get Next Line, append it if the first charactor is space
239 if(GetLine(NextLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
240 RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
241 RequestInfo.ThreadNum) != 0) return GH_ERROR;
242 if ((NextLine[0] == ' ') || (NextLine[0] == '\t'))
243 strcat(CurLine, NextLine);
244 } while ((NextLine[0] == ' ') || (NextLine[0] == '\t'));
245 //Method String (first word)
246 Start = 0;
247 GetWord(RequestFields.MethodStr, CurLine, Start, End);
248 CharUpper(RequestFields.MethodStr);
249
250 //Version String (last word)
251 GetLastWord(RequestFields.VersionStr, CurLine, Start);
252 CharUpper(RequestFields.VersionStr);
253
254 if (strncmp(RequestFields.VersionStr, "HTTP/", 5) != 0) {
255 //No version, assume simple request
256 //part after method is URI
257 for (int i = 0; i < strlen(CurLine); i++) {
258 RequestFields.URIStr.push_back(CurLine[i]);
259 }
260 return GH_SIMPLE_REQUEST;
261 }
262
263 //URI String (in between End of first and Start of last)
264 //<Method> <WhiteSpace> <URI> <WhiteSpace> <Version> <CRLF>
265 // End^ Start^
266 text_t spacebuffer;
267 for (int i = End; i < Start; i++) {
268 // do this to remove trailing space
269 if (CurLine[i] == ' ') {
270 spacebuffer.push_back(' ');
271 } else {
272 if (!spacebuffer.empty()) {
273 RequestFields.URIStr += spacebuffer;
274 spacebuffer.clear();
275 }
276 RequestFields.URIStr.push_back(CurLine[i]);
277 }
278 }
279
280 //Only accept requests from HTTP/0.9 or HTTP/1.X clients, we'll
281 //assume that anything else will require an upgrade or patch
282 if (strncmp(RequestFields.VersionStr, "HTTP/1.", 7) != 0)
283 return GH_UNKNOWN_VERSION;
284
285 //Get the rest of the lines
286
287 strcpy(CurLine, NextLine);
288
289 while (CurLine[0] != 0) {//Blank Line, we're done
290 do {//Get Next Line, append it if the first charactor is space
291 if (GetLine(NextLine, RequestInfo.ClientSocket, RequestInfo.IOBuffer,
292 RequestInfo.IOBufferSize, ReadBufferIndex, DataInBuffer,
293 RequestInfo.ThreadNum) != 0)
294 return GH_ERROR;
295 if ((NextLine[0] == ' ') || (NextLine[0] == '\t'))
296 strcat(CurLine, NextLine);
297 } while ((NextLine[0] == ' ') || (NextLine[0] == '\t'));
298
299 Start = 0;
300 GetWord(FieldNameStr, CurLine, Start, End);
301 CharUpper(FieldNameStr);
302
303 Len = strlen(CurLine) - End;
304 memcpy(FieldValStr, CurLine + End, Len);
305 FieldValStr[Len] = 0;
306
307 //Process it
308 //In order of expected commonality
309 //All constants are in canonized, thus in upper case and case sensitive
310 //comparisons are used
311 //--Just About Always--
312 if (strcmp("ACCEPT:", FieldNameStr) == 0) {
313 if (RequestFields.AcceptStr[0] == '\0') {
314 strncpy(RequestFields.AcceptStr, FieldValStr, ReqAcceptStrLen - 1);
315 }
316 else {
317 //Append it with a comma
318 int AcceptStrLen = strlen(RequestFields.AcceptStr);
319 if ((ReqAcceptStrLen - AcceptStrLen) >= 10) {
320 strncat(RequestFields.AcceptStr, ", ", ReqAcceptStrLen - AcceptStrLen - 1);
321 strncat(RequestFields.AcceptStr, FieldValStr, ReqAcceptStrLen - AcceptStrLen - 3);
322 }
323 }
324 }
325 else if (strcmp("DATE:", FieldNameStr) == 0) {
326 strncpy(RequestFields.DateStr, FieldValStr, ReqDateStrLen - 1);
327 }
328 else if (strcmp("USER-AGENT:", FieldNameStr) == 0) {
329 strncpy(RequestFields.UserAgentStr, FieldValStr, ReqUserAgentStrLen - 1);
330 }
331 else if (strcmp("CONNECTION:", FieldNameStr) == 0) {
332 strncpy(RequestFields.ConnectionStr, FieldValStr, ReqConnectionStrLen - 1);
333 }
334 //--Sometimes--
335 else if (strcmp("ACCEPT-LANGUAGE:", FieldNameStr) == 0) {
336 strncpy(RequestFields.AcceptLangStr, FieldValStr, ReqAcceptLangStrLen - 1);
337 }
338 else if (strcmp("REFERER:", FieldNameStr) == 0) {
339 strncpy(RequestFields.RefererStr, FieldValStr, ReqRefererStrLen - 1);
340 }
341 else if (strcmp("IF-MODIFIED-SINCE:", FieldNameStr) == 0) {
342 strncpy(RequestFields.IfModSinceStr, FieldValStr, ReqIfModSinceStrLen - 1);
343 }
344 //--Uncommon--
345 else if (strcmp("FROM:", FieldNameStr) == 0) {
346 strncpy(RequestFields.FromStr, FieldValStr, ReqFromStrLen - 1);
347 }
348 else if (strcmp("MIME-VERSION:", FieldNameStr) == 0) {
349 strncpy(RequestFields.MIMEVerStr, FieldValStr, ReqMIMEVerStrLen - 1);
350 }
351 else if (strcmp("PRAGMA:", FieldNameStr) == 0) {
352 strncpy(RequestFields.PragmaStr, FieldValStr, ReqPragmaStrLen - 1);
353 }
354 //--Special case--
355 else if (strcmp("AUTHORIZATION:", FieldNameStr) == 0) {
356 strncpy(RequestFields.AuthorizationStr, FieldValStr, ReqAuthorizationStrLen - 1);
357 }
358 else if (strcmp("CONTENT-LENGTH:", FieldNameStr) == 0) {
359 strncpy(RequestFields.ContentLengthStr, FieldValStr, ReqContentLengthStrLen - 1);
360 }
361 else if (strcmp("CONTENT-TYPE:", FieldNameStr) == 0) {
362 strncpy(RequestFields.ContentTypeStr, FieldValStr, ReqContentTypeStrLen - 1);
363 }
364 else if (strcmp("CONTENT-ENCODING:", FieldNameStr) == 0) {
365 strncpy(RequestFields.ContentEncodingStr, FieldValStr, ReqContentEncodingStrLen - 1);
366 }
367 else {
368 //Add it to the other headers
369 int VarLen = strlen(FieldNameStr);
370 if (FieldNameStr[VarLen - 1] == ':') {
371 //Remove the colon
372 FieldNameStr[VarLen - 1] = '\0';
373 VarLen--;
374 }
375 RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var = new char[VarLen + 1];
376 RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val = new char[Len + 1];
377 strcpy(RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Var, FieldNameStr);
378 strcpy(RequestFields.OtherHeaders[RequestFields.NumOtherHeaders].Val, FieldValStr);
379 RequestFields.NumOtherHeaders++;
380 }
381 strcpy(CurLine, NextLine);
382 }
383
384 if (RequestFields.ContentLengthStr[0] != 0) { //Do we have attached data?
385 unsigned int NumRecv;
386
387 RequestFields.ContentLength = atol(RequestFields.ContentLengthStr);
388 if (RequestFields.ContentLength > 0) {
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 // It seems to be important on NT that all available data was read
412 // from the socket before the socket is closed (otherwise netscape
413 // throws a "connection reset by peer" error). Since netscape seems
414 // to send a few extra bytes in certain situations we'll make sure we
415 // slurp it all up here.
416 char *tmpbuffer = new char(100);
417 d_recv(RequestInfo.ClientSocket, tmpbuffer, 100, 0);
418 delete tmpbuffer;
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.