file_http.cpp revision 1.1 1 /* $NetBSD: file_http.cpp,v 1.1 2001/02/09 18:34:38 uch Exp $ */
2
3 /*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <file.h>
40 #include <file_http.h>
41 #include <console.h>
42
43 static int __stricmp(const char *, const char *);
44
45 HttpFile::HttpFile(Console *&cons)
46 : File(cons),
47 _req_get("GET "),
48 _req_head("HEAD "),
49 _req_host(" HTTP/1.0\r\nHOST: "),
50 _req_ua("\r\nUser-Agent: HPCBOOT/ZERO(1st impact; Windows CE; "
51 #if defined MIPS
52 "MIPS"
53 #elif defined ARM
54 "ARM"
55 #elif defined SH3
56 "SH3"
57 #elif defined SH4
58 "SH4"
59 #else
60 "Unknown"
61 #endif
62 ")\r\n\r\n")
63 {
64 _server_name[0] = '\0';
65 _debug = TRUE;
66 _memory_cache = TRUE;
67 // _memory_cache = FALSE; // not recomended.
68 _buffer = 0;
69 _reset_state();
70 DPRINTF((TEXT("File: HTTP\n")));
71 }
72
73 HttpFile::~HttpFile(void)
74 {
75 if (_buffer)
76 free(_buffer);
77 WSACleanup();
78 }
79
80 void
81 HttpFile::_reset_state(void)
82 {
83 _ascii_filename[0] = '\0';
84 _cached = FALSE;
85 if (_buffer)
86 free(_buffer);
87 _buffer = 0;
88 _header_size = 0;
89 _cur_pos = 0;
90 }
91
92 BOOL
93 HttpFile::setRoot(TCHAR *server)
94 {
95 SOCKET h;
96 int ret, port;
97
98 // parse server name and its port #
99 TCHAR sep[] = TEXT(":/");
100 TCHAR *token = wcstok(server, sep);
101 for (int i = 0; i < 3 && token; i++, token = wcstok(0, sep)) {
102 switch(i) {
103 case 0:
104 if (wcsicmp(token, TEXT("http"))) {
105 return FALSE;
106 }
107 break;
108 case 1:
109 if (!_to_ascii(_server_name, token, MAX_PATH))
110 return FALSE;
111 port = 80;
112 break;
113 case 2:
114 port = _wtoi(token);
115 break;
116 }
117 }
118
119 ret = WSAStartup(MAKEWORD(1, 1), &_winsock);
120 if (ret != 0) {
121 DPRINTF((TEXT("WinSock initialize failed.\n")));
122 return FALSE;
123 }
124 if (LOBYTE(_winsock.wVersion) != 1 ||
125 HIBYTE(_winsock.wVersion) != 1) {
126 DPRINTF((TEXT("can't use WinSock DLL.\n")));
127 return FALSE;
128 }
129
130 h = socket(AF_INET, SOCK_STREAM, 0);
131 if (h == INVALID_SOCKET) {
132 DPRINTF((TEXT("can't open socket. cause=%d\n"),
133 WSAGetLastError()));
134 return FALSE;
135 }
136
137 struct hostent *entry = gethostbyname(_server_name);
138 if (entry == 0) {
139 DPRINTF((TEXT("can't get host by name.\n")));
140 return FALSE;
141 }
142
143 memset(&_sockaddr, 0, sizeof(sockaddr_in));
144 _sockaddr.sin_family = AF_INET;
145 _sockaddr.sin_port = htons(port);
146 for (u_int8_t **addr_list =(u_int8_t **)entry->h_addr_list;
147 *addr_list; addr_list++) {
148 u_int8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1;
149 for (int i = 0; i < 4; i++)
150 b[i] = addr_list[0][i];
151
152 DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2], b[3]));
153 if (connect(h,(const struct sockaddr *)&_sockaddr,
154 sizeof(struct sockaddr_in)) == 0)
155 goto connected;
156 }
157 DPRINTF((TEXT("can't connect server.\n")));
158 return FALSE;
159
160 connected:
161 DPRINTF((TEXT(" \"%S\"\n"), _server_name));
162 closesocket(h);
163
164 return TRUE;
165 }
166
167 BOOL
168 HttpFile::open(const TCHAR *name, u_int32_t flag)
169 {
170 _reset_state();
171 return _to_ascii(_ascii_filename, name, MAX_PATH);
172 }
173
174 size_t
175 HttpFile::_read_from_cache(void *buf, size_t bytes, off_t ofs)
176 {
177 size_t transfer = ofs + bytes > _buffer_size
178 ? _buffer_size - ofs : bytes;
179 memcpy(buf, &_buffer[ofs], transfer);
180 return transfer;
181 }
182
183 BOOL
184 HttpFile::seek(off_t offset)
185 {
186 _cur_pos = offset;
187
188 return TRUE;
189 }
190
191 size_t
192 HttpFile::read(void *buf, size_t bytes, off_t offset)
193 {
194 char *b;
195 off_t ofs;
196
197 if (offset != -1) {
198 ofs = offset;
199 } else {
200 ofs = _cur_pos;
201 _cur_pos += bytes;
202 }
203
204 if (_memory_cache && _cached)
205 return _read_from_cache(buf, bytes, ofs);
206
207 // HEAD request(get header size).
208 if (_header_size == 0)
209 _buffer_size = _parse_header(_header_size);
210
211 // reconnect
212 Socket sock(_sockaddr);
213 SOCKET h;
214 if ((h = sock) == INVALID_SOCKET)
215 return 0;
216
217 // GET request
218 strcpy(_request, _req_get);
219 _set_request();
220 send(h, _request, strlen(_request), 0);
221
222 // skip header.
223 b = static_cast <char *>(malloc(_header_size));
224 _recv_buffer(h, b, _header_size);
225 free(b);
226
227 // read contents.
228 size_t readed;
229 if (_memory_cache) {
230 _buffer = static_cast <char *>(malloc(_buffer_size));
231 _recv_buffer(h, _buffer, _buffer_size);
232 _cached = TRUE;
233 return _read_from_cache(buf, bytes, ofs);
234 } else {
235 int i, n = ofs / bytes;
236 b = static_cast <char *>(buf);
237
238 for (readed = 0, i = 0; i < n; i++)
239 readed += _recv_buffer(h, b, bytes);
240 if ((n =(ofs % bytes)))
241 readed += _recv_buffer(h, b, n);
242 DPRINTF((TEXT("skip contents %d byte.\n"), readed));
243
244 readed = _recv_buffer(h, b, bytes);
245 }
246 return readed;
247 }
248
249 size_t
250 HttpFile::_parse_header(size_t &header_size)
251 {
252 size_t sz = 0;
253 // reconnect.
254 Socket sock(_sockaddr);
255 SOCKET h;
256 if ((h = sock) == INVALID_SOCKET)
257 return 0;
258
259 // HEAD request
260 strcpy(_request, _req_head);
261 _set_request();
262 send(h, _request, strlen(_request), 0);
263
264 // receive.
265 char __buf[TMP_BUFFER_SIZE];
266 int cnt, ret;
267
268 for (cnt = 0;
269 ret = _recv_buffer(h, __buf, TMP_BUFFER_SIZE); cnt += ret) {
270 char sep[] = " :\r\n";
271 char *token = strtok(__buf, sep);
272 while (token) {
273 if (__stricmp(token, "content-length") == 0) {
274 token = strtok(0, sep);
275 sz = atoi(token);
276 DPRINTF((TEXT("content-length=%d\n"), sz));
277 } else
278 token = strtok(0, sep);
279 }
280 }
281 header_size = cnt;
282 DPRINTF((TEXT("header %d byte contents %d byte\n"), header_size, sz));
283
284 return sz;
285 }
286
287 size_t
288 HttpFile::_recv_buffer(SOCKET h, char *buf, size_t size)
289 {
290 size_t cnt, total = 0;
291
292 do {
293 cnt = recv(h, buf + total, size - total, 0);
294 total += cnt;
295 DPRINTFN(2,(TEXT("size %d readed %d byte(+%d)\n"),
296 size, total, cnt));
297 } while (total < size && cnt > 0);
298
299
300 DPRINTFN(1,(TEXT("total read %d byte\n"), total));
301 return total;
302 }
303
304 void
305 HttpFile::_set_request(void)
306 {
307 strcat(_request, _ascii_filename);
308 strcat(_request, _req_host);
309 strcat(_request, _server_name);
310 strcat(_request, _req_ua);
311 }
312
313 static int
314 __stricmp(const char *s1, const char *s2)
315 {
316 const unsigned char *us1 =
317 reinterpret_cast <const unsigned char *>(s1);
318 const unsigned char *us2 =
319 reinterpret_cast <const unsigned char *>(s2);
320
321 while (tolower(*us1) == tolower(*us2++))
322 if (*us1++ == '\0')
323 return 0;
324 return tolower(*us1) - tolower(*--us2);
325 }
326
327 Socket::Socket(struct sockaddr_in &sock)
328 : _sockaddr(sock)
329 {
330 _socket = socket(AF_INET, SOCK_STREAM, 0);
331 if (_socket != INVALID_SOCKET)
332 connect(_socket,
333 reinterpret_cast <const struct sockaddr *>(&_sockaddr),
334 sizeof(struct sockaddr_in));
335 }
336
337 Socket::~Socket(void)
338 {
339 if (_socket != INVALID_SOCKET)
340 closesocket(_socket);
341 }
342