ObjFW  Check-in [3beecbace3]

Overview
Comment:Add OFHTTPRequest class.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 3beecbace3c75ae87ecfeb9f36c165dc040315c6386137d6813469f65d5ad6a7
User & Date: js on 2011-02-09 16:16:08
Other Links: manifest | tags
Context
2011-02-09
16:35
Don't allow nil as argument for -[appendString:]. check-in: a3fef2953d user: js tags: trunk
16:16
Add OFHTTPRequest class. check-in: 3beecbace3 user: js tags: trunk
12:33
Add support for relative URLs to OFURL. check-in: ddd3a6683f user: js tags: trunk
Changes

Modified ObjFW.xcodeproj/project.pbxproj from [600fafbad2] to [d2c945e2d8].

192
193
194
195
196
197
198


199
200
201
202
203
204
205
		4B6EF67F1235358D0076B512 /* PropertiesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PropertiesTests.m; path = tests/PropertiesTests.m; sourceTree = SOURCE_ROOT; };
		4B6EF6801235358D0076B512 /* TestsAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestsAppDelegate.h; path = tests/TestsAppDelegate.h; sourceTree = SOURCE_ROOT; };
		4B6EF6811235358D0076B512 /* TestsAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestsAppDelegate.m; path = tests/TestsAppDelegate.m; sourceTree = SOURCE_ROOT; };
		4B6EF684123535B60076B512 /* TestPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestPlugin.m; path = tests/plugin/TestPlugin.m; sourceTree = SOURCE_ROOT; };
		4B6EF685123535C80076B512 /* test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = test.m; path = tests/objc_sync/test.m; sourceTree = SOURCE_ROOT; };
		4B981CDE116F71DD00294DB7 /* OFSeekableStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSeekableStream.h; path = src/OFSeekableStream.h; sourceTree = "<group>"; };
		4B981CDF116F71DD00294DB7 /* OFSeekableStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSeekableStream.m; path = src/OFSeekableStream.m; sourceTree = "<group>"; };


		4BAF5F46123460C900F4E111 /* OFCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCollection.h; path = src/OFCollection.h; sourceTree = "<group>"; };
		4BAF5F47123460C900F4E111 /* OFStreamObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFStreamObserver.h; path = src/OFStreamObserver.h; sourceTree = "<group>"; };
		4BAF5F48123460C900F4E111 /* OFStreamObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFStreamObserver.m; path = src/OFStreamObserver.m; sourceTree = "<group>"; };
		4BAF5F49123460C900F4E111 /* OFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFStreamSocket.h; path = src/OFStreamSocket.h; sourceTree = "<group>"; };
		4BAF5F4A123460C900F4E111 /* OFStreamSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFStreamSocket.m; path = src/OFStreamSocket.m; sourceTree = "<group>"; };
		4BB50DCF12F863C700C9393F /* of_asprintf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = of_asprintf.h; path = src/of_asprintf.h; sourceTree = SOURCE_ROOT; };
		4BB50DD012F863C700C9393F /* of_asprintf.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = of_asprintf.m; path = src/of_asprintf.m; sourceTree = SOURCE_ROOT; };







>
>







192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
		4B6EF67F1235358D0076B512 /* PropertiesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PropertiesTests.m; path = tests/PropertiesTests.m; sourceTree = SOURCE_ROOT; };
		4B6EF6801235358D0076B512 /* TestsAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestsAppDelegate.h; path = tests/TestsAppDelegate.h; sourceTree = SOURCE_ROOT; };
		4B6EF6811235358D0076B512 /* TestsAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestsAppDelegate.m; path = tests/TestsAppDelegate.m; sourceTree = SOURCE_ROOT; };
		4B6EF684123535B60076B512 /* TestPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TestPlugin.m; path = tests/plugin/TestPlugin.m; sourceTree = SOURCE_ROOT; };
		4B6EF685123535C80076B512 /* test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = test.m; path = tests/objc_sync/test.m; sourceTree = SOURCE_ROOT; };
		4B981CDE116F71DD00294DB7 /* OFSeekableStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSeekableStream.h; path = src/OFSeekableStream.h; sourceTree = "<group>"; };
		4B981CDF116F71DD00294DB7 /* OFSeekableStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSeekableStream.m; path = src/OFSeekableStream.m; sourceTree = "<group>"; };
		4B99250F12E0780000215DBE /* OFHTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFHTTPRequest.h; path = src/OFHTTPRequest.h; sourceTree = "<group>"; };
		4B99251012E0780000215DBE /* OFHTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFHTTPRequest.m; path = src/OFHTTPRequest.m; sourceTree = "<group>"; };
		4BAF5F46123460C900F4E111 /* OFCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFCollection.h; path = src/OFCollection.h; sourceTree = "<group>"; };
		4BAF5F47123460C900F4E111 /* OFStreamObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFStreamObserver.h; path = src/OFStreamObserver.h; sourceTree = "<group>"; };
		4BAF5F48123460C900F4E111 /* OFStreamObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFStreamObserver.m; path = src/OFStreamObserver.m; sourceTree = "<group>"; };
		4BAF5F49123460C900F4E111 /* OFStreamSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFStreamSocket.h; path = src/OFStreamSocket.h; sourceTree = "<group>"; };
		4BAF5F4A123460C900F4E111 /* OFStreamSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFStreamSocket.m; path = src/OFStreamSocket.m; sourceTree = "<group>"; };
		4BB50DCF12F863C700C9393F /* of_asprintf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = of_asprintf.h; path = src/of_asprintf.h; sourceTree = SOURCE_ROOT; };
		4BB50DD012F863C700C9393F /* of_asprintf.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = of_asprintf.m; path = src/of_asprintf.m; sourceTree = SOURCE_ROOT; };
273
274
275
276
277
278
279


280
281
282
283
284
285
286
				4B0108CA10EB8C9300631877 /* OFEnumerator.m */,
				4B6799641099E7C50041064A /* OFExceptions.h */,
				4B6799651099E7C50041064A /* OFExceptions.m */,
				4B6799661099E7C50041064A /* OFFile.h */,
				4B6799671099E7C50041064A /* OFFile.m */,
				4BF1BCC011C9663F0025511F /* OFHash.h */,
				4BF1BCC111C9663F0025511F /* OFHash.m */,


				4B67996C1099E7C50041064A /* OFList.h */,
				4B67996D1099E7C50041064A /* OFList.m */,
				4BF1BCC211C9663F0025511F /* OFMD5Hash.h */,
				4BF1BCC311C9663F0025511F /* OFMD5Hash.m */,
				4B67996F1099E7C50041064A /* OFMutableArray.h */,
				4B6799701099E7C50041064A /* OFMutableArray.m */,
				4B6799711099E7C50041064A /* OFMutableDictionary.h */,







>
>







275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
				4B0108CA10EB8C9300631877 /* OFEnumerator.m */,
				4B6799641099E7C50041064A /* OFExceptions.h */,
				4B6799651099E7C50041064A /* OFExceptions.m */,
				4B6799661099E7C50041064A /* OFFile.h */,
				4B6799671099E7C50041064A /* OFFile.m */,
				4BF1BCC011C9663F0025511F /* OFHash.h */,
				4BF1BCC111C9663F0025511F /* OFHash.m */,
				4B99250F12E0780000215DBE /* OFHTTPRequest.h */,
				4B99251012E0780000215DBE /* OFHTTPRequest.m */,
				4B67996C1099E7C50041064A /* OFList.h */,
				4B67996D1099E7C50041064A /* OFList.m */,
				4BF1BCC211C9663F0025511F /* OFMD5Hash.h */,
				4BF1BCC311C9663F0025511F /* OFMD5Hash.m */,
				4B67996F1099E7C50041064A /* OFMutableArray.h */,
				4B6799701099E7C50041064A /* OFMutableArray.m */,
				4B6799711099E7C50041064A /* OFMutableDictionary.h */,

Modified src/Makefile from [c370e44199] to [cc40692929].

13
14
15
16
17
18
19

20
21
22
23
24
25
26
       OFDataArray.m		\
       OFDataArray+Hashing.m	\
       OFDate.m			\
       OFDictionary.m		\
       OFExceptions.m		\
       OFFile.m			\
       OFHash.m			\

       OFEnumerator.m		\
       OFList.m			\
       OFMD5Hash.m		\
       OFMutableArray.m		\
       OFMutableDictionary.m	\
       OFMutableString.m	\
       OFNumber.m		\







>







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
       OFDataArray.m		\
       OFDataArray+Hashing.m	\
       OFDate.m			\
       OFDictionary.m		\
       OFExceptions.m		\
       OFFile.m			\
       OFHash.m			\
       OFHTTPRequest.m		\
       OFEnumerator.m		\
       OFList.m			\
       OFMD5Hash.m		\
       OFMutableArray.m		\
       OFMutableDictionary.m	\
       OFMutableString.m	\
       OFNumber.m		\

Modified src/OFExceptions.h from [65b9bf22dd] to [015ac7b5d3].

16
17
18
19
20
21
22

23
24
25
26
27
28
29

#include <sys/types.h>

#import "OFObject.h"

@class OFString;
@class OFURL;


/**
 * \brief An exception indicating an object could not be allocated.
 *
 * This exception is preallocated, as if there's no memory, no exception can
 * be allocated of course. That's why you shouldn't and even can't deallocate
 * it.







>







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

#include <sys/types.h>

#import "OFObject.h"

@class OFString;
@class OFURL;
@class OFHTTPRequest;

/**
 * \brief An exception indicating an object could not be allocated.
 *
 * This exception is preallocated, as if there's no memory, no exception can
 * be allocated of course. That's why you shouldn't and even can't deallocate
 * it.
1281
1282
1283
1284
1285
1286
1287





















































	    URL: (OFURL*)url;

/**
 * \return The URL whose protocol is unsupported
 */
- (OFURL*)URL;
@end




























































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
	    URL: (OFURL*)url;

/**
 * \return The URL whose protocol is unsupported
 */
- (OFURL*)URL;
@end

/**
 * \brief An exception indicating that the server sent an invalid reply.
 */
@interface OFInvalidServerReplyException: OFException
@end

/**
 * \brief An exception indicating that a HTTP request failed.
 */
@interface OFHTTPRequestFailedException: OFException
{
	OFHTTPRequest *HTTPRequest;
	short statusCode;
}

#ifdef OF_HAVE_PROPERTIES
@property (readonly, nonatomic) OFHTTPRequest *HTTPRequest;
@property (readonly) short statusCode;
#endif

/**
 * \param class_ The class of the object which caused the exception
 * \param request The HTTP request which failed
 * \param code The status code of the fialed HTTP request
 * \return A new HTTP request failed exception
 */
+ newWithClass: (Class)class_
   HTTPRequest: (OFHTTPRequest*)request
     statusCode: (short)code;

/**
 * Initializes an already allocated HTTP request failed exception
 *
 * \param class_ The class of the object which caused the exception
 * \param request The HTTP request which failed
 * \param code The status code of the fialed HTTP request
 * \return A new HTTP request failed exception
 */
- initWithClass: (Class)class_
    HTTPRequest: (OFHTTPRequest*)request
     statusCode: (short)code;

/**
 * \return The HTTP request which failed
 */
- (OFHTTPRequest*)HTTPRequest;

/**
 * \return The status code of the HTTP request
 */
- (short)statusCode;
@end

Modified src/OFExceptions.m from [2044606b5e] to [66659ddd45].

30
31
32
33
34
35
36


37
38
39
40
41
42
43
# define sel_getName(x) sel_get_name(x)
# define class_getName class_get_class_name
#endif

#import "OFExceptions.h"
#import "OFString.h"
#import "OFTCPSocket.h"



#ifndef _WIN32
# include <errno.h>
# define GET_ERRNO	errno
# ifndef HAVE_THREADSAFE_GETADDRINFO
#  define GET_AT_ERRNO	h_errno
# else







>
>







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# define sel_getName(x) sel_get_name(x)
# define class_getName class_get_class_name
#endif

#import "OFExceptions.h"
#import "OFString.h"
#import "OFTCPSocket.h"
#import "OFHTTPRequest.h"
#import "OFAutoreleasePool.h"

#ifndef _WIN32
# include <errno.h>
# define GET_ERRNO	errno
# ifndef HAVE_THREADSAFE_GETADDRINFO
#  define GET_AT_ERRNO	h_errno
# else
1883
1884
1885
1886
1887
1888
1889


































































































}

- (OFURL*)URL
{
	return URL;
}
@end









































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
}

- (OFURL*)URL
{
	return URL;
}
@end

@implementation OFInvalidServerReplyException
- (OFString*)description
{
	if (description != nil)
		return description;

	description = [[OFString alloc] initWithFormat:
	    @"Got an invalid reply from the server in class %s",
	    class_getName(inClass)];

	return description;
}
@end

@implementation OFHTTPRequestFailedException
+ newWithClass: (Class)class_
   HTTPRequest: (OFHTTPRequest*)request
    statusCode: (short)code
{
	return [[self alloc] initWithClass: class_
			       HTTPRequest: request
				statusCode: code];
}

- initWithClass: (Class)class_
{
	Class c = isa;
	[self release];
	@throw [OFNotImplementedException newWithClass: c
					      selector: _cmd];
}

- initWithClass: (Class)class_
    HTTPRequest: (OFHTTPRequest*)request
     statusCode: (short)code
{
	self = [super initWithClass: class_];

	@try {
		HTTPRequest = [request retain];
		statusCode = code;
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[HTTPRequest release];

	[super dealloc];
}

- (OFString*)description
{
	OFAutoreleasePool *pool;
	const char *type = "(unknown)";

	if (description != nil)
		return description;

	switch ([HTTPRequest requestType]) {
	case OF_HTTP_REQUEST_TYPE_GET:
		type = "GET";
		break;
	case OF_HTTP_REQUEST_TYPE_HEAD:
		type = "HEAD";
		break;
	case OF_HTTP_REQUEST_TYPE_POST:
		type = "POST";
		break;
	}

	pool = [[OFAutoreleasePool alloc] init];

	description = [[OFString alloc] initWithFormat:
	    @"A HTTP %s request of class %s with URL %@ failed with code %d",
	    type, class_getName(inClass), [HTTPRequest URL], statusCode];

	[pool release];

	return description;
}

- (OFHTTPRequest*)HTTPRequest
{
	return HTTPRequest;
}

- (short)statusCode
{
	return statusCode;
}
@end

Added src/OFHTTPRequest.h version [49c4f75181].



























































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/*
 * Copyright (c) 2008, 2009, 2010, 2011
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OFObject.h"

@class OFString;
@class OFDictionary;
@class OFURL;
@class OFHTTPRequestResult;
@class OFDataArray;

typedef enum of_http_request_type_t {
	OF_HTTP_REQUEST_TYPE_GET,
	OF_HTTP_REQUEST_TYPE_POST,
	OF_HTTP_REQUEST_TYPE_HEAD
} of_http_request_type_t;

/**
 * \brief A class for storing and performing HTTP requests.
 */
@interface OFHTTPRequest: OFObject
{
	OFURL *URL;
	of_http_request_type_t requestType;
	OFString *queryString;
	OFDictionary *headers;
}

#ifdef OF_HAVE_PROPERTIES
@property (copy) OFURL *URL;
@property (assign) of_http_request_type_t requestType;
@property (copy) OFString *queryString;
@property (copy) OFDictionary *headers;
#endif

/**
 * \return A new, autoreleased OFHTTPRequest
 */
+ request;

/**
 * Sets the URL for the HTTP request.
 *
 * \param URL The URL for the HTTP request
 */
- (void)setURL: (OFURL*)url;

/**
 * \return The URL for the HTTP request
 */
- (OFURL*)URL;

/**
 * Sets the request type for the HTTP request.
 *
 * \param type The request type for the HTTP request
 */
- (void)setRequestType: (of_http_request_type_t)type;

/**
 * \return The request type for the HTTP request
 */
- (of_http_request_type_t)requestType;

/**
 * Sets the query string for the HTTP request.
 * Only used for GET and HEAD requests!
 *
 * \param qs The query string for the HTTP request
 */
- (void)setQueryString: (OFString*)qs;

/**
 * \return The query string for the HTTP request
 */
- (OFString*)queryString;

/**
 * Sets a dictionary with headers for the HTTP request.
 *
 * \param headers A dictionary with headers for the HTTP request
 */
- (void)setHeaders: (OFDictionary*)headers;

/**
 * \return A dictionary with headers for the HTTP request.
 */
- (OFDictionary*)headers;

/**
 * Performs the HTTP request and returns an OFHTTPRequestResult.
 *
 * \return An OFHTTPRequestResult with the result of the HTTP request
 */
- (OFHTTPRequestResult*)result;

/**
 * Performs the HTTP request and returns an OFHTTPRequestResult.
 *
 * \param redirects The maximum number of redirects after which no further
 *		    attempt is done to follow the redirect, but instead the
 *		    redirect is returned as an OFHTTPRequest
 * \return An OFHTTPRequestResult with the result of the HTTP request
 */
- (OFHTTPRequestResult*)resultWithRedirects: (size_t)redirects;
@end

/**
 * \brief A class for storing the result of an HTTP request.
 */
@interface OFHTTPRequestResult: OFObject
{
	short statusCode;
	OFDataArray *data;
	OFDictionary *headers;
}

#ifdef OF_HAVE_PROPERTIES
@property (readonly) short statusCode;
@property (readonly, copy) OFDictionary *headers;
@property (readonly, retain) OFDataArray *data;
#endif

/// \cond internal
- initWithStatusCode: (short)status
	     headers: (OFDictionary*)headers
		data: (OFDataArray*)data;
/// \endcond

/**
 * \return The status code of the result of the HTTP request
 */
- (short)statusCode;

/**
 * \return The HTTP headers of the result of the HTTP request
 */
- (OFDictionary*)headers;

/**
 * \return The data returned for the HTTP request
 */
- (OFDataArray*)data;
@end

Added src/OFHTTPRequest.m version [38ccb7c9e6].









































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
 * Copyright (c) 2008, 2009, 2010, 2011
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#include <string.h>

#import "OFHTTPRequest.h"
#import "OFString.h"
#import "OFURL.h"
#import "OFTCPSocket.h"
#import "OFDictionary.h"
#import "OFAutoreleasePool.h"
#import "OFExceptions.h"

@implementation OFHTTPRequest
+ request
{
	return [[[self alloc] init] autorelease];
}

- init
{
	self = [super init];

	requestType = OF_HTTP_REQUEST_TYPE_GET;
	headers = [OFDictionary
	    dictionaryWithObject: @"Something using ObjFW "
				  @"<https://webkeks.org/objfw/>"
			  forKey: @"User-Agent"];

	return self;
}

- (void)dealloc
{
	[URL release];
	[queryString release];
	[headers release];

	[super dealloc];
}

- (void)setURL: (OFURL*)url
{
	OFURL *old = URL;
	URL = [url copy];
	[old release];
}

- (OFURL*)URL
{
	return [[URL copy] autorelease];
}

- (void)setRequestType: (of_http_request_type_t)type
{
	requestType = type;
}

- (of_http_request_type_t)requestType
{
	return requestType;
}

- (void)setQueryString: (OFString*)qs
{
	OFString *old = queryString;
	queryString = [qs copy];
	[old release];
}

- (OFString*)queryString
{
	return [[queryString copy] autorelease];
}

- (void)setHeaders: (OFDictionary*)headers_
{
	OFDictionary *old = headers;
	headers = [headers_ copy];
	[old release];
}

- (OFDictionary*)headers
{
	return [[headers copy] autorelease];
}

- (OFHTTPRequestResult*)result
{
	return [self resultWithRedirects: 10];
}

- (OFHTTPRequestResult*)resultWithRedirects: (size_t)redirects
{
	OFAutoreleasePool *pool;
	OFTCPSocket *sock;
	OFHTTPRequestResult *result;

	if (![[URL scheme] isEqual: @"http"])
		@throw [OFUnsupportedProtocolException newWithClass: isa
								URL: URL];
	pool = [[OFAutoreleasePool alloc] init];
	sock = [OFTCPSocket socket];

	[sock connectToService: [OFString stringWithFormat: @"%d", [URL port]]
			onNode: [URL host]];

	@try {
		OFString *line;
		OFMutableDictionary *s_headers;
		OFDataArray *data;
		OFEnumerator *enumerator;
		OFString *key;
		int status;
		char *t;

		if (requestType == OF_HTTP_REQUEST_TYPE_GET)
			t = "GET";
		if (requestType == OF_HTTP_REQUEST_TYPE_HEAD)
			t = "HEAD";
		if (requestType == OF_HTTP_REQUEST_TYPE_POST)
			t = "POST";

		if ([URL query] != nil)
			[sock writeFormat: @"%s /%@?%@ HTTP/1.0\r\n",
					   t, [URL path], [URL query]];
		else
			[sock writeFormat: @"%s /%@ HTTP/1.0\r\n",
					   t, [URL path]];

		if ([URL port] == 80)
			[sock writeFormat: @"Host: %@\r\n", [URL host]];
		else
			[sock writeFormat: @"Host: %@:%d\r\n", [URL host],
					   [URL port]];

		enumerator = [headers keyEnumerator];

		while ((key = [enumerator nextObject]) != nil)
			[sock writeFormat: @"%@: %@\r\n",
					   key, [headers objectForKey: key]];

		if (requestType == OF_HTTP_REQUEST_TYPE_POST) {
			if ([headers objectForKey: @"Content-Type"] == nil)
				[sock writeString: @"Content-Type: "
				   @"application/x-www-form-urlencoded\r\n"];

			if ([headers objectForKey: @"Content-Length"] == nil)
				[sock writeFormat: @"Content-Length: %d\r\n",
				    [queryString cStringLength]];
		}

		[sock writeString: @"\r\n"];

		if (requestType == OF_HTTP_REQUEST_TYPE_POST)
			[sock writeString: queryString];

		/*
		 * We also need to check for HTTP/1.1 since Apache always
		 * declares the reply to be HTTP/1.1.
		 */
		line = [sock readLine];
		if (![line hasPrefix: @"HTTP/1.0 "] &&
		    ![line hasPrefix: @"HTTP/1.1 "])
			@throw [OFInvalidServerReplyException
			    newWithClass: isa];

		status = [[line substringFromIndex: 9
					   toIndex: 12] decimalValue];

		if (status != 200 && status != 301 && status != 302 &&
		    status != 303)
			@throw [OFHTTPRequestFailedException
			    newWithClass: isa
			     HTTPRequest: self
			      statusCode: status];

		s_headers = [OFMutableDictionary dictionary];

		while ((line = [sock readLine]) != nil) {
			OFString *key, *value;
			const char *line_c = [line cString], *tmp;

			if ([line isEqual: @""])
				break;

			if ((tmp = strchr(line_c, ':')) == NULL)
				@throw [OFInvalidServerReplyException
				    newWithClass: isa];

			key = [OFString stringWithCString: line_c
						   length: tmp - line_c];

			do {
				tmp++;
			} while (*tmp == ' ');

			value = [OFString stringWithCString: tmp];

			if (redirects > 0 && (status == 301 || status == 302 ||
			    status == 303) && [key caseInsensitiveCompare:
			    @"Location"] == OF_ORDERED_SAME) {
				OFURL *new;

				new = [[OFURL alloc] initWithString: value
						      relativeToURL: URL];
				[URL release];
				URL = new;

				if (status == 303) {
					requestType = OF_HTTP_REQUEST_TYPE_GET;
					[queryString release];
					queryString = nil;
				}

				[pool release];
				pool = nil;

				return [self resultWithRedirects:
				    redirects - 1];
			}

			[s_headers setObject: value
				      forKey: key];
		}

		data = [[sock readDataArrayTillEndOfStream] retain];

		result = [[OFHTTPRequestResult alloc]
		    initWithStatusCode: status
			       headers: s_headers
				  data: data];
	} @finally {
		[pool release];
	}

	return [result autorelease];
}
@end

@implementation OFHTTPRequestResult
- initWithStatusCode: (short)status
	     headers: (OFDictionary*)headers_
		data: (OFDataArray*)data_
{
	self = [super init];

	statusCode = status;
	data = [data_ retain];
	headers = [headers_ copy];

	return self;
}

- (void)dealloc
{
	[data release];
	[headers release];

	[super dealloc];
}

- (short)statusCode
{
	return statusCode;
}

- (OFDictionary*)headers
{
	return [[headers copy] autorelease];
}

- (OFDataArray*)data
{
	return [[data retain] autorelease];
}
@end

Modified src/ObjFW.h from [f0cdbd0d51] to [312401f872].

36
37
38
39
40
41
42


43
44
45
46
47
48
49
#import "OFURL.h"

#import "OFStream.h"
#import "OFFile.h"
#import "OFStreamSocket.h"
#import "OFTCPSocket.h"
#import "OFStreamObserver.h"



#import "OFHash.h"
#import "OFMD5Hash.h"
#import "OFSHA1Hash.h"

#import "OFXMLAttribute.h"
#import "OFXMLElement.h"







>
>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#import "OFURL.h"

#import "OFStream.h"
#import "OFFile.h"
#import "OFStreamSocket.h"
#import "OFTCPSocket.h"
#import "OFStreamObserver.h"

#import "OFHTTPRequest.h"

#import "OFHash.h"
#import "OFMD5Hash.h"
#import "OFSHA1Hash.h"

#import "OFXMLAttribute.h"
#import "OFXMLElement.h"