libs-gsweb/GSWeb/GSWHTTPIO.m

442 lines
12 KiB
Mathematica
Raw Normal View History

/** GSWHTTPIO.m - GSWeb: Class GSWHTTPIO
Copyright (C) 2007 Free Software Foundation, Inc.
Written by: David Wetzel <dave@turbocat.de>
Date: 12.11.2007
This file is part of the GNUstep Web Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**/
#include "GSWHTTPIO.h"
#include <Foundation/NSString.h>
#include <Foundation/NSFileHandle.h>
#include <Foundation/NSData.h>
* GSWExtensionsGSW.framework/GSWFileUploadComponent.m * GSWExtensionsGSW.framework/GSWLogin.m removed logs * GSWExtensionsGSW.framework/GNUmakefile added ADDITIONAL_OBJCFLAGS = -Werror-implicit-function-declaration * GSWExtensionsGSW.framework/GSWCacheElement.m removed logs, use object_getClassName * GSWExtensionsGSW.framework/GSWValidationFailureComponent.m * GSWExtensionsGSW.framework/GSWTabComponent.m * GSWExtensionsGSW.framework/GSWFileUploadFormComponent.m * GSWExtensionsGSW.framework/GSWSimpleFormComponent.m * GSWeb.framework/GSWApplication.m removed logs, _validateAPI ClassIsKindOfClass -> GSObjCIsKindOf * GSWeb.framework/GSWAction.m include <GNUstepBase/NSObject+GNUstepBase.h> removed logs and unused (commented) code * GSWeb.framework/GSWResourceManager.h removed _validateAPI * GSWeb.framework/GSWImageButton.m added #include <GNUstepBase/NSObject+GNUstepBase.h> removed logs and unused (commented) code * GSWeb.framework/NSString+HTML.h changed int to NSUInteger / NSInteger * GSWeb.framework/GSWResourceManager.m removed logs, _validateAPI * GSWeb.framework/GSWContext.m LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWRequestHandler.m include <GNUstepBase/NSObject+GNUstepBase.h> LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/NSString+HTML.m include <GNUstepBase/NSString+GNUstepBase.h> changed int to NSUInteger use memmove in GSWMemMove macro disabled testStringByConvertingHTML removed logs fixed warnings * GSWeb.framework/GSWMultiKeyDictionary.m removed logs * GSWeb.framework/GSWVBScript.m removed logs * GSWeb.framework/NSNonBlockingFileHandle.m removed logs * GSWeb.framework/GSWStatisticsStore.h removed useless @interface / @end for the same class * GSWeb.framework/GSWLifebeatThread.m removed include <GNUstepBase/GSCategories.h> removed logs * GSWeb.framework/GSWStatisticsStore.m removed logs include <GNUstepBase/NSObject+GNUstepBase.h> include <GNUstepBase/NSString+GNUstepBase.h> implement _purgePathsStatistics, _updatePathsStatisticsWithPaths removed useless @implementation / @end for the same class * GSWeb.framework/GSWCookie.m removed logs * GSWeb.framework/GNUmakefile added ADDITIONAL_OBJCFLAGS = -Werror-implicit-function-declaration * GSWeb.framework/GSWRecording.m removed logs * GSWeb.framework/GSWToggle.m removed logs * GSWeb.framework/GSWMessage.m include <GNUstepBase/NSObject+GNUstepBase.h> globalDefaultEncoding = [NSString defaultCStringEncoding]; (see http://wiki.gnustep.org/index.php/GSWebFAQ ) use NSUInteger for index LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWDefaultAdaptor.m include <GNUstepBase/NSObject+GNUstepBase.h> LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWJavaScript.m * GSWeb.framework/GSWClientSideScript.m removed logs * GSWeb.framework/GSWNestedList.m removed logs LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWEmbeddedObject.m LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWRequest.h removed _validateAPI * GSWeb.framework/GSWRequest.m add includes removed _validateAPI removed logs LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWUtils.h unsigned int -> NSUInteger * GSWeb.framework/Resources/languages.plist added languages * GSWeb.framework/GSWUtils.m add includes unsigned int -> NSUInteger removed logs * GSWeb.framework/GSWComponentReference.m removed logs * GSWeb.framework/GSWGeometricRegion.m add includes removed logs * GSWeb.framework/GSWKeyValueAssociation.m add includes removed logs LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWHiddenField.m removed logs * GSWeb.framework/GSWParam.m add includes removed logs LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/NSString+Trimming.h * GSWeb.framework/NSString+Trimming.m removed longValue, ulongValue, longLongValue; removed logs * GSWeb.framework/GSWStaticResourceRequestHandler.m removed logs * GSWeb.framework/GSWURLValuedElementData.m removed logs * GSWeb.framework/GSWDirectAction.m takeValue -> setValue removed logs int -> NSUInteger * GSWeb.framework/GSWComponentDefinition.m add includes removed logs LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWAssociation.m add includes LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; removed logs * GSWeb.framework/GSWSession.h * GSWeb.framework/GSWSession.m unsigned int-> NSUInteger add includes LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; removed logs David Ayers: pls check sleepInContext! removed _validateAPI, __setContextCounterIncrementingEnabled, __counterIncrementingEnabledFlag * GSWeb.framework/GSWFileUpload.m removed logs * GSWeb.framework/GSWApplet.m add includes LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWGenericElement.m removed logs GSWeb.framework/GSWTemplateParser.m add includes * GSWeb.framework/GSWTemporaryElement.m removed logs * GSWeb.framework/GSWResponse.m add include removed docStructure checks removed logs * GSWeb.framework/GSWProjectBundle.m add include LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; removed logs * GSWeb.framework/GSWWorkerThread.m added - drain removed NSLog * GSWeb.framework/GSWApplication+Defaults.m add include LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; removed logs * GSWeb.framework/GSWActiveImage.m * GSWeb.framework/GSWDirectActionRequestHandler.m * GSWeb.framework/GSWConstantValueAssociation.m * GSWeb.framework/NSData+Compress.m removed logs * GSWeb.framework/GSWHTMLStaticElement.m add includes LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; removed logs * GSWeb.framework/GSWServerSessionStore.m removed logs * GSWeb.framework/GSWAdaptor.m add includes LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWDeployedBundle.m removed logs add includes * GSWeb.framework/GSWSubmitButton.m removed logs * GSWeb.framework/GSWDeclarationParser.m add includes removed logs * GSWeb.framework/GSWBundle.m add includes removed logs clearCache * GSWeb.framework/GSWWOCompatibility.h fixed typo in WOMessage * GSWeb.framework/GSWSessionTimeOut.m removed logs * GSWeb.framework/GSWPopUpButton.m add includes * GSWeb.framework/GSWComponent.m add includes LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWHTTPIO.m add include * GSWeb.framework/GSWBindingNameAssociation.m takeValue -> setValue * GSWeb.framework/GSWHTMLTemplateParser.m removed logs * GSWeb.framework/GSWResetButton.m removed logs * GSWeb.framework/GSWDebug.h/m disabled log funtions * GSWeb.framework/GSWActionRequestHandler.m add includes removed logs * GSWeb.framework/GSWSwitchComponent.m * GSWeb.framework/GSWSessionTimeOutManager.m removed logs * GSWeb.framework/GSWMailDelivery.m add includes removed logs * GSWeb.framework/GSWDynamicURLString.h * GSWeb.framework/GSWDynamicURLString.m unsigned int -> NSUInteger removed checkURL add includes * GSWeb.framework/GSWProcFS.m removed logs * GSWeb.framework/GSWApplication.h removed _validateAPI * GSWeb.framework/GSWResourceRequestHandler.m add include removed logs LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; * GSWeb.framework/GSWSessionStore.h removed _validateAPI * GSWeb.framework/GSWSessionStore.m add include removed _validateAPI removed logs * GSWExtensions.framework/GSWLongResponsePage.m * GSWExtensions.framework/GSWExceptionPage.m * GSWExtensions.framework/GSWMetaRefresh.m * GSWExtensions.framework/GSWCollapsibleComponentContent.m removed logs * GSWExtensions.framework/GNUmakefile added ADDITIONAL_OBJCFLAGS = -Werror-implicit-function-declaration * GSWExtensions.framework/GSWStatsPage.m removed logs unsigned int -> NSUInteger * Examples/hello/HelloPage.wo/HelloPage.html * Examples/hello/Main.wo/Main.html * Examples/hello/Hello.m convert to UTF8 The change of LOGObjectFnNotImplemented(); -> [self notImplemented: _cmd]; will show where we need to implement functionality. Please send bug reports! -- dw git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gsweb/trunk@30584 72102866-910b-0410-8b05-ffd578937521
2010-06-05 21:08:12 +00:00
#include <GNUstepBase/NSString+GNUstepBase.h>
#include <Foundation/NSError.h>
#include <Foundation/NSException.h>
#include <Foundation/NSDictionary.h>
#include <Foundation/Foundation.h>
#include "GSWDefines.h"
#include "GSWConstants.h"
#include "GSWUtils.h"
#include "GSWDebug.h"
#include "GSWMessage.h"
#include "GSWResponse.h"
#include "GSWRequest.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#define READ_SIZE 2048
static NSString *URIResponseString = @" GNUstep Web\r\n";
static NSString *CONTENT_LENGTHCOLON = @"Content-Length: ";
static NSString *GET = @"GET";
static NSString *POST = @"POST";
static NSString *HEAD = @"HEAD";
static NSString *SPACE = @" ";
static NSString *HEADERSEP = @": ";
static NSString *NEWLINE = @"\r\n";
static NSString *NEWLINE2 = @"\r\n";
static NSString *HTTP11 = @"HTTP/1.1";
static NSString *CONNECTION = @"Connection";
static NSString *KEEP_ALIVE = @"keep-alive";
static BOOL _alwaysAppendContentLength = YES;
//static NSString *CLOSE = @"close";
/* Get error information.
*/
@interface NSError (GSCategories)
+ (NSError*) _last;
@end
@interface NSFileHandle (GSWFileHandleExtensions)
//- (void) setNonBlocking: (BOOL)flag;
- (NSData*) readDataLine;
@end
@implementation NSFileHandle (GSWFileHandleExtensions)
- (NSData*) readDataLine
{
NSMutableData *d;
int got,pos=0;
char buf[READ_SIZE];
int fileDescriptor = [self fileDescriptor];
d = [NSMutableData dataWithCapacity: READ_SIZE];
do {
got = recv(fileDescriptor, &buf[pos], 1, 0);
if (got > 0) {
if (buf[pos] != 0xd) { // CR
if (buf[pos] == 0xa) { // NL
break;
}
pos++;
}
} else if (got < 0) {
[NSException raise: NSFileHandleOperationException
format: @"unable to read from descriptor - %@",
[NSError _last]];
}
} while ((got > 0) && (pos < READ_SIZE));
if (pos>0) {
[d appendBytes: buf length: pos];
}
return d;
}
@end
@implementation GSWHTTPIO
/* Apple is accessing this in Application.java in wotaskd example code -- dw */
+ (void) _setAlwaysAppendContentLength:(BOOL) yn
{
_alwaysAppendContentLength = yn;
}
+ (BOOL) _alwaysAppendContentLength
{
return _alwaysAppendContentLength;
}
// PRIVATE
void _unpackHeaderLineAddToDict(NSString *line, NSMutableDictionary* headers)
{
NSArray * components = [line componentsSeparatedByString:HEADERSEP];
NSString * value = nil;
NSArray * newValue = nil;
NSString * key = nil;
NSArray * prevValue = nil;
if ((components) && ([components count] == 2)) {
value = [components objectAtIndex:1];
key = [components objectAtIndex:0];
key = [key stringByTrimmingSpaces];
if ([key isEqualToString:GSWHTTPHeader_AdaptorVersion[GSWNAMES_INDEX]]
|| [key isEqualToString:GSWHTTPHeader_ServerName[GSWNAMES_INDEX]]) {
// _requestNamingConv=GSWNAMES_INDEX;
goto keyDone;
}
if ([key isEqualToString:GSWHTTPHeader_AdaptorVersion[WONAMES_INDEX]]
|| [key isEqualToString:GSWHTTPHeader_ServerName[WONAMES_INDEX]]) {
// _requestNamingConv=WONAMES_INDEX;
goto keyDone;
}
keyDone:
prevValue=[headers objectForKey:key];
if (prevValue) {
newValue=[prevValue arrayByAddingObject:value];
} else {
newValue=[NSArray arrayWithObject:value];
}
[headers setObject: newValue
forKey: key];
}
}
//PRIVATE
void _appendMessageHeaders(GSWResponse * message,NSMutableString * headers)
{
NSMutableDictionary * headerDict = [message headers];
NSArray * keyArray = nil;
int i = 0;
if (headerDict != nil) {
int count = 0;
if (![headerDict isKindOfClass:[NSMutableDictionary class]]) {
headerDict = [[headerDict mutableCopy] autorelease];
}
[headerDict removeObjectForKey:GSWHTTPHeader_ContentLength];
keyArray = [headerDict allKeys];
count = [keyArray count];
for (; i < count; i++) {
NSString * currentKey = [keyArray objectAtIndex:i];
NSArray * currentValueArray = [headerDict objectForKey:currentKey];
if ([currentValueArray isKindOfClass:[NSArray class]]) {
int x = 0;
int valueCount = [currentValueArray count];
for (; x < valueCount; x++) {
[headers appendString:currentKey];
[headers appendString:HEADERSEP];
[headers appendString:[currentValueArray objectAtIndex:x]];
[headers appendString:NEWLINE];
}
} else {
NSString * myStrValue = (NSString*) currentValueArray;
[headers appendString:currentKey];
[headers appendString:HEADERSEP];
[headers appendString:myStrValue];
[headers appendString:NEWLINE];
}
}
}
}
//PRIVATE
void _sendMessage(GSWResponse * message, NSFileHandle* fh, NSString * httpVersion, GSWRequest * request, NSMutableString * headers)
{
int contentLength = 0;
BOOL keepAlive = NO;
BOOL requestIsHead = NO;
if (message) {
contentLength = [message _contentLength];
}
if (request) {
NSString * connectionValue = [request headerForKey:CONNECTION];
if (connectionValue) {
keepAlive = [connectionValue isEqualToString:KEEP_ALIVE];
}
requestIsHead = [[request method] isEqualToString:HEAD];
}
[headers appendString:GSWIntToNSString([message status])];
[headers appendString:URIResponseString];
_appendMessageHeaders(message,headers);
if ([httpVersion isEqualToString:HTTP11]) {
// bug #24006 keep-alive is not implemented.
// I am uable to reproduce the need for double clicking on links/forms,
// but for now, we send close. -- dw
if (YES /*keepAlive == NO*/) {
[headers appendString:@"Connection: close\r\n"];
} else {
[headers appendString:@"Connection: keep-alive\r\n"];
}
}
if ((contentLength > 0) || _alwaysAppendContentLength) {
[headers appendString:CONTENT_LENGTHCOLON];
[headers appendString:[NSString stringWithFormat:@"%d\r\n", contentLength]];
}
[headers appendString:NEWLINE2];
[fh writeData: [headers dataUsingEncoding:NSISOLatin1StringEncoding
allowLossyConversion:YES]];
if (((requestIsHead == NO) && (contentLength > 0)) &&
([message status] != 304)) {
[fh writeData: [message content]];
}
}
//PRIVATE
+ (void) _getConnectionInfoFromHandle:(NSFileHandle*) fh
remoteAddress:(NSString**) rAddress
remotePort:(uint16_t*) rPort
{
int fileDescriptor = [fh fileDescriptor];
struct sockaddr_in sockAddress;
socklen_t address_len = sizeof(sockAddress);
char str[INET_ADDRSTRLEN];
if (getpeername(fileDescriptor, (struct sockaddr *) &sockAddress, &address_len) == 0) {
inet_ntop(AF_INET, &sockAddress.sin_addr, str, sizeof(str));
*rAddress = [NSString stringWithUTF8String:str];
*rPort = ntohs(sockAddress.sin_port);
}
}
+ (NSDictionary*) readHeadersFromHandle:(NSFileHandle*) fh
{
NSData *currentLineData = nil;
unsigned int length = 0;
NSMutableDictionary *headers = [NSMutableDictionary dictionary];
NSString * tmpString = nil;
NS_DURING {
while (YES) {
currentLineData = [fh readDataLine];
length = [currentLineData length];
if (length == 0) {
break;
}
tmpString=[[NSString alloc] initWithData: currentLineData
encoding:NSASCIIStringEncoding];
_unpackHeaderLineAddToDict(tmpString,headers);
[tmpString release]; tmpString = nil;
}
} NS_HANDLER {
NSLog(@"%s -- %@",__PRETTY_FUNCTION__, localException);
if (tmpString != nil) {
[tmpString release]; tmpString = nil;
}
headers = nil;
} NS_ENDHANDLER;
return headers;
}
// GET /infotext.html HTTP/1.1
// Host: www.example.net
+ (NSArray*) readRequestLineFromHandle:(NSFileHandle*) fh
{
NSString * tmpString = nil;
NSArray * components = nil;
NSData * currentLineData = nil;
int length = 0;
NS_DURING {
currentLineData = [fh readDataLine];
length = [currentLineData length];
if (length > 0) {
tmpString=[[NSString alloc] initWithData: currentLineData
encoding:NSASCIIStringEncoding];
components = [tmpString componentsSeparatedByString:@" "];
[tmpString release]; tmpString = nil;
}
} NS_HANDLER {
NSLog(@"%s -- %@",__PRETTY_FUNCTION__, localException);
if (tmpString != nil) {
[tmpString release]; tmpString = nil;
}
components = nil;
} NS_ENDHANDLER;
return components;
}
+ (NSData*) readContentFromFromHandle: fh
method: (NSString *) method
length: (int) length
{
NSData* data = nil;
if ((([method isEqualToString:GET]) || ([method isEqualToString:HEAD])) ||
([method isEqualToString:POST] == NO || (length <1))) {
NSLog(@"%s: unsupportet method '%@'", __PRETTY_FUNCTION__, method);
return nil;
}
data = [fh readDataOfLength: length];
return data;
}
+ (GSWRequest*) readRequestFromFromHandle:(NSFileHandle*) fh
{
NSDictionary * headers;
NSArray * requestArray;
int contentLength = 0;
NSArray * tmpValue = nil;
NSString * method = nil;
NSData * contentData = nil;
GSWRequest * request = nil;
uint16_t rPort = 0;
NSString * rAddress = nil;
// checkme.
//#ifdef GNUSTEP
// [(GSFileHandle*) fh setNonBlocking: NO];
//#endif
// get info about who talks to us
[self _getConnectionInfoFromHandle: fh
remoteAddress: &rAddress
remotePort: &rPort];
requestArray = [self readRequestLineFromHandle:fh];
if ((!requestArray) || ([requestArray count] <3)) {
return nil;
}
headers = [self readHeadersFromHandle:fh];
if (!headers) {
return nil;
}
method = [requestArray objectAtIndex:0];
if ((tmpValue = [headers objectForKey:GSWHTTPHeader_ContentLength]) && ([tmpValue count])) {
NSString * tmpString = [tmpValue objectAtIndex:0];
contentLength = [tmpString intValue];
contentData = [self readContentFromFromHandle: fh
method: [requestArray objectAtIndex:0]
length: contentLength];
}
request = [[GSWRequest alloc] initWithMethod:method
uri:[requestArray objectAtIndex:1]
httpVersion:[requestArray objectAtIndex:2]
headers:headers
content:contentData
userInfo:nil];
if (request != nil)
{
[request _setOriginatingAddress:rAddress];
[request _setOriginatingPort:rPort];
// [request _setAcceptingAddress:xxx];
// [request _setAcceptingPort:yyy];
}
return [request autorelease];
}
+ (void) sendResponse:(GSWResponse*) response
toHandle:(NSFileHandle*) fh
request:(GSWRequest*) request
{
NSString * httpVersion = [response httpVersion];
NSMutableString * bufferStr = [NSMutableString string];
[bufferStr appendString:httpVersion];
[bufferStr appendString:SPACE];
_sendMessage(response, fh, httpVersion, request, bufferStr);
}
@end