1// Sun, 18 Jun 2000
2// Y.Takagi
3
4#include <list>
5#include <algorithm>
6#include <cstring>
7#include <strings.h>
8
9#define stricmp	strcasecmp
10
11#include "HttpURLConnection.h"
12#include "Socket.h"
13
14using namespace std;
15
16#define DEFAULT_PORT	80;
17
18Field::Field(char *field)
19{
20	char *p = strtok(field, ": \t\r\n");
21	key = p ? p : "";
22	p = strtok(NULL,   " \t\r\n");
23	value = p ? p : "";
24}
25
26Field::Field(const char *k, const char *v)
27{
28	key   = k ? k : "";
29	value = v ? v : "";
30}
31
32Field::Field(const Field &o)
33{
34	key   = o.key;
35	value = o.value;
36}
37
38Field &Field::operator = (const Field &o)
39{
40	key   = o.key;
41	value = o.value;
42	return *this;
43}
44
45bool Field::operator == (const Field &o)
46{
47	return (key == o.key) && (value == o.value);
48}
49
50HttpURLConnection::HttpURLConnection(const BUrl &Url)
51	: connected(false), doInput(true), doOutput(false), url(Url)
52{
53	__sock     = NULL;
54	__method   = "GET";
55	__request  = NULL;
56	__response = NULL;
57	__response_code = HTTP_UNKNOWN;
58}
59
60HttpURLConnection::~HttpURLConnection()
61{
62	disconnect();
63
64	if (__sock) {
65		delete __sock;
66	}
67
68	if (__request) {
69		delete __request;
70	}
71
72	if (__response) {
73		delete __response;
74	}
75}
76
77void HttpURLConnection::disconnect()
78{
79	if (connected) {
80		connected = false;
81		__sock->close();
82	}
83}
84
85const char *HttpURLConnection::getRequestMethod() const
86{
87	return __method.c_str();
88}
89
90void HttpURLConnection::setRequestMethod(const char *method)
91{
92	__method = method;
93}
94
95void HttpURLConnection::setRequestProperty(const char *key, const char *value)
96{
97	if (__request == NULL) {
98		__request = new Fields;
99	}
100	__request->push_back(Field(key, value));
101}
102
103istream &HttpURLConnection::getInputStream()
104{
105	if (!connected) {
106		connect();
107		setRequest();
108	}
109	return __sock->getInputStream();
110}
111
112ostream &HttpURLConnection::getOutputStream()
113{
114	if (!connected) {
115		connect();
116	}
117	return __sock->getOutputStream();
118}
119
120void HttpURLConnection::setDoInput(bool doInput)
121{
122	this->doInput = doInput;
123}
124
125void HttpURLConnection::setDoOutput(bool doOutput)
126{
127	this->doOutput = doOutput;
128}
129
130void HttpURLConnection::connect()
131{
132	if (!connected) {
133		int port = url.Port();
134		if (port < 0) {
135			const char *protocol = url.Protocol();
136			if (!stricmp(protocol, "http")) {
137				port = DEFAULT_PORT;
138			} else if (!stricmp(protocol, "ipp")) {
139				port = 631;
140			} else {
141				port = DEFAULT_PORT;
142			}
143		}
144		__sock = new Socket(url.Host(), port);
145		if (__sock->fail()) {
146			__error_msg = __sock->getLastError();
147		} else {
148			connected = true;
149		}
150	}
151}
152
153const char *HttpURLConnection::getContentType()
154{
155	return getHeaderField("Content-Type");
156}
157
158const char *HttpURLConnection::getContentEncoding()
159{
160	return getHeaderField("Content-Encoding");
161}
162
163int HttpURLConnection::getContentLength()
164{
165	const char *p = getHeaderField("Content-Length");
166	return p ? atoi(p) : -1;
167}
168
169const char *HttpURLConnection::getHeaderField(const char *s)
170{
171	if (__response == NULL) {
172		action();
173	}
174	if (__response) {
175		for (Fields::iterator it = __response->begin(); it != __response->end(); it++) {
176			if ((*it).key == s) {
177				return (*it).value.c_str();
178			}
179		}
180	}
181	return NULL;
182}
183
184HTTP_RESPONSECODE HttpURLConnection::getResponseCode()
185{
186	if (__response == NULL) {
187		action();
188	}
189	return __response_code;
190}
191
192const char *HttpURLConnection::getResponseMessage()
193{
194	if (__response == NULL) {
195		action();
196	}
197	return __response_message.c_str();
198}
199
200void HttpURLConnection::action()
201{
202	if (!connected) {
203		connect();
204	}
205	if (connected) {
206		setRequest();
207	}
208	if (connected) {
209		getResponse();
210	}
211}
212
213void HttpURLConnection::setRequest()
214{
215	if (connected) {
216		setRequestProperty("Host", url.Host());
217		ostream &os = getOutputStream();
218		os << __method << ' ' << url.Path() << " HTTP/1.1" << '\r' << '\n';
219		for (Fields::iterator it = __request->begin(); it != __request->end(); it++) {
220			os << (*it).key << ": " << (*it).value << '\r' << '\n';
221		}
222		os << '\r' << '\n';
223
224		setContent();
225
226		if (!doOutput) {
227			os.flush();
228		}
229
230		if (__response) {
231			delete __response;
232			__response = NULL;
233		}
234	}
235}
236
237void HttpURLConnection::setContent()
238{
239}
240
241void HttpURLConnection::getResponse()
242{
243	if (connected) {
244
245		if (__response == NULL) {
246			__response = new Fields;
247
248			istream &is = getInputStream();
249
250			char buffer[1024];
251
252			if (!is.getline(buffer, sizeof(buffer))) {
253				__error_msg = __sock->getLastError();
254				return;
255			}
256			buffer[is.gcount() - 2] = '\0';
257			__response_message = buffer;
258			strtok(buffer, " ");
259			char *p = strtok(NULL, " ");
260			__response_code = p ? (HTTP_RESPONSECODE)atoi(p) : HTTP_UNKNOWN;
261
262			while (is.getline(buffer, sizeof(buffer))) {
263				if (buffer[0] == '\r') {
264					break;
265				}
266				buffer[is.gcount() - 2] = '\0';
267				__response->push_back(Field(buffer));
268			}
269
270			int size = getContentLength();
271			if (size > 0) {
272				getContent();
273			}
274
275			if (__response_code != HTTP_CONTINUE) {
276				const char *s = getHeaderField("Connection");
277				if (s == NULL) {
278					connected = false;
279					__error_msg = "cannot found \"Connection\" field";
280				} else if (stricmp(s, "Keep-Alive")) {
281					connected = false;
282				}
283			}
284
285			switch (__response_code) {
286			case HTTP_MOVED_TEMP:
287				{
288					const char *p = getHeaderField("Location");
289					if (p) {
290						BUrl trueUrl(p);
291						url = trueUrl;
292						delete __response;
293						__response = NULL;
294						action();
295					}
296				}
297				break;
298			case HTTP_CONTINUE:
299				delete __response;
300				__response = NULL;
301				getResponse();
302				break;
303			default:
304				break;
305			}
306		}
307	}
308}
309
310void HttpURLConnection::getContent()
311{
312	const int maxBufSize = 1024;
313	if (connected) {
314		int size = getContentLength();
315		if (size > 0) {
316			istream &is = getInputStream();
317			int bufsize = min(size, maxBufSize);
318			char buf[maxBufSize];
319			while (size > 0 && is.read(buf, bufsize)) {
320				size -= bufsize;
321			}
322		}
323	}
324}
325