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