OSDN Git Service

HLS試作
[iptd/iPTd.git] / src / Raym / URLConnection.cpp
1 //
2 // URLConnection.cpp
3 //
4
5 #ifdef _WIN32
6 #include <windows.h>
7 #include <io.h>
8 #else
9 #include <sys/socket.h>
10 #include <netdb.h>
11 #include <unistd.h>
12 #endif
13
14 #define DBG_LEVEL 0
15 #include <Raym/Log.h>
16 #include <Raym/URLConnection.h>
17 #include <Raym/HTTPURLResponse.h>
18
19 namespace Raym
20 {
21
22 URLConnection::URLConnection()
23 {
24 }
25
26 URLConnection::~URLConnection()
27 {
28 }
29
30 //
31 // socketからterminatorで指定する終端文字列に遭遇するまで読み込む。
32 // 返却したバッファは必ずfreeすること。
33 // 失敗した場合はNULLを返却する。
34 //
35 #ifdef _WIN32
36 static char *readLine(SOCKET socket, const char *terminator)
37 #else
38 static char *readLine(int socket, const char *terminator)
39 #endif
40 {
41     char *buf = NULL;
42     if ((socket >= 0) && (terminator != NULL) && (strlen(terminator) > 0))
43     {
44         static const int BUF_UNIT = 256;
45         long long unit_count = 0;
46         long long term_len = strlen(terminator);
47         long long offset = 0;
48         bool done = false;
49         while (true)
50         {
51             // バッファ確保
52             if (offset == (unit_count * BUF_UNIT))
53             {
54                 ++unit_count;
55                 char *newbuf = (char *)realloc(buf, (unit_count * BUF_UNIT));
56                 if (newbuf == NULL)
57                 {
58                     if (buf != NULL)
59                     {
60                         free(buf);
61                         buf = NULL;
62                         printf("realloc ng.\n");
63                     }
64                     break;
65                 }
66                 buf = newbuf;
67             }
68
69             if (done)
70             {
71                 buf[offset] = '\0';
72                 break;
73             }
74
75             // 1文字読み込み
76             int len = recv(socket, &buf[offset], 1, 0);
77             if (len <= 0)
78             {
79                 if (offset == 0)
80                 {
81                     free(buf);
82                     buf = NULL;
83                 }
84                 if (len < 0)
85                 {
86                     printf("recv error.\n");
87                 }
88                 break;
89             }
90             if (offset >= (term_len - 1))
91             {
92                 if (memcmp(&buf[offset - term_len + 1], terminator, term_len) == 0)
93                 {
94                     done = true;
95                 }
96             }
97             ++offset;
98         }
99     }
100     return buf;
101 }
102
103 #ifdef _WIN32
104 static HTTPURLResponse *receiveHTTPURLResponse(SOCKET socket, URL *url)
105 #else
106 static HTTPURLResponse *receiveHTTPURLResponse(int socket, URL *url)
107 #endif
108 {
109     HTTPURLResponse *result = NULL;
110
111     if (socket >= 0)
112     {
113         // Status Line の読み込み
114         char *statusLine;
115         while (true)
116         {
117             statusLine = readLine(socket, "\r\n");
118             if (statusLine == NULL)
119             {
120                 DebugLog3("error: read status line.\n");
121                 return NULL;
122             }
123             if (strlen(statusLine) > 0)
124             {
125                 break;
126             }
127
128             DebugLog3("error: read status line. (retry)\n");
129             free(statusLine);
130             statusLine = NULL;
131         }
132
133         char *p = strchr(statusLine, ' ');
134         if ((p == NULL) || ((strncmp("HTTP/1.0 ", statusLine, 9) != 0) && (strncmp("HTTP/1.1 ", statusLine, 9) != 0)))
135         {
136             DebugLog3("error: invalid format. (HTTP-Version) \"%s\"\n", statusLine);
137             free(statusLine);
138             return NULL;
139         }
140         *p = '\0';
141
142         String *http_version = String::stringWithUTF8String(statusLine);
143
144         char *code = p + 1;
145         p = strchr(code, ' ');
146         if (p == NULL)
147         {
148             DebugLog3("error: invalid format. (Status-Code) \"%s\"\n", statusLine);
149             free(statusLine);
150             return NULL;
151         }
152         *p = '\0';
153
154         int status_code = atoi(code);
155         char tmp[256];
156         sprintf_s(tmp, sizeof(tmp), "%d", status_code);
157         if (strcmp(code, tmp) != 0)
158         {
159             DebugLog3("error: invalid format. (Status-Code) \"%s\"\n", statusLine);
160             free(statusLine);
161             return NULL;
162         }
163
164         char *reason = p + 1;
165         p = strstr(reason, "\r\n");
166         if (p == NULL)
167         {
168             DebugLog3("error: invalid format. (Reason-Phrase) \"%s\"\n", statusLine);
169             free(statusLine);
170             return NULL;
171         }
172         *p = '\0';
173
174         String *reason_phrase = String::stringWithUTF8String(reason);
175
176         free(statusLine);
177
178         DebugLog3("status line: %s %d %s\n", http_version->cString(), status_code, reason_phrase->cString());
179
180         Dictionary *headers = Dictionary::dictionaryWithCapacity(0);
181
182         if (http_version->isEqualToString("HTTP/1.1"))
183         {
184             // ヘッダ読み込み
185             while (true)
186             {
187                 char *header_line = readLine(socket, "\r\n");
188                 if (header_line == NULL)
189                 {
190                     break;
191                 }
192                 if (strcmp("\r\n", header_line) == 0)
193                 {
194                     free(header_line);
195                     break;
196                 }
197
198                 char *p = strchr(header_line, ':');
199                 if (p != NULL)
200                 {
201                     *p = '\0';
202                     char *value = p + 1;
203                     while (*value == ' ')
204                     {
205                         ++value;
206                     }
207                     p = strstr(value, "\r\n");
208                     *p = '\0';
209                     headers->setString(value, header_line);
210                     DebugLog3("%s: %s\n", header_line, value);
211                 }
212
213                 free(header_line);
214             }
215         }
216         else
217         {
218         }
219         result = HTTPURLResponse::alloc()->initWithURL(url, status_code, http_version, headers);
220         if (result != NULL)
221         {
222             result->autorelease();
223         }
224     }
225     return result;
226 }
227
228 #ifdef _WIN32
229 static Data *receiveData(SOCKET socket, long long expectedContentLength)
230 #else
231 static Data *receiveData(int socket, long long expectedContentLength)
232 #endif
233 {
234     DebugLog2("receiveData(%d, %lld)\n", socket, expectedContentLength);
235
236     Data *result = NULL;
237
238     char *buf = NULL;
239     if (socket >= 0)
240     {
241         static const int BUF_UNIT = 1024;
242         long long unit_count = 0;
243
244         long long offset = 0;
245         while (true)
246         {
247             // バッファ確保
248             if (offset == (unit_count * BUF_UNIT))
249             {
250                 ++unit_count;
251                 char *newbuf = (char *)realloc(buf, (unit_count * BUF_UNIT));
252                 if (newbuf == NULL)
253                 {
254                     if (buf != NULL)
255                     {
256                         free(buf);
257                         buf = NULL;
258                     }
259                     break;
260                 }
261                 buf = newbuf;
262             }
263
264             // 読み込み
265             int rdlen;
266             if (expectedContentLength == URLResponseUnknownLength)
267             {
268                 rdlen = BUF_UNIT;
269             }
270             else if ((expectedContentLength - offset) > BUF_UNIT)
271             {
272                 rdlen = BUF_UNIT;
273             }
274             else
275             {
276                 rdlen = (expectedContentLength - offset);
277             }
278             size_t len = recv(socket, &buf[offset], rdlen, 0);
279             if (len == 0)
280             {
281                 // 終端 or エラー
282                 break;
283             }
284
285             offset += len;
286
287             if (expectedContentLength != URLResponseUnknownLength)
288             {
289                 if (len != rdlen)
290                 {
291                     // 終端 or エラー
292                     break;
293                 }
294                 if (offset >= expectedContentLength)
295                 {
296                     break;
297                 }
298             }
299         }
300         if (buf != NULL)
301         {
302             result = Data::alloc()->initWithBytesAndLength(buf, offset);
303             free(buf);
304             if (result != NULL)
305             {
306                 result->autorelease();
307             }
308         }
309     }
310     return result;
311 }
312
313 Data *URLConnection::sendSynchronousRequest(URLRequest *request, URLResponse **response, Error **error)
314 {
315     DebugLog2("URLConnection::sendSynchronousRequest()\n");
316
317     //
318     // 現状は、更なるクラス群を調査する気力が無いので
319     // socketで直接実装する
320     // とりあえず linux socket で実装。あとでwinsockに対応
321     //
322
323     Data *result = NULL;
324     if (request != NULL)
325     {
326         URL *url = request->url();
327         if (url->scheme()->isEqualToString("http"))
328         {
329             // http のみ実装
330             struct sockaddr_in dst_addr;
331             dst_addr.sin_family = AF_INET;
332             dst_addr.sin_port = htons(url->port());
333             struct hostent *hostent = gethostbyname(url->host()->cString());
334             if (hostent == NULL)
335             {
336                 DebugLog3("error: gethostbyname().\n");
337                 return NULL;
338             }
339 #ifdef _WIN32
340             dst_addr.sin_addr.s_addr = *(unsigned int *)hostent->h_addr;
341 #else
342             dst_addr.sin_addr.s_addr = *(uint32_t *)hostent->h_addr;
343 #endif
344
345 #ifdef _WIN32
346 //            WSADATA wsaData;
347 //            WSAStartup(MAKEWORD(2,0), &wsaData);
348             SOCKET sock = INVALID_SOCKET;
349             sock = socket(AF_INET, SOCK_STREAM, 0);
350             if (sock == INVALID_SOCKET)
351 #else
352             int sock;
353             sock = socket(AF_INET, SOCK_STREAM, 0);
354             if (sock == -1)
355 #endif
356             {
357                 DebugLog3("error: socket()\n");
358                 return NULL;
359             }
360
361             if (connect(sock, (const struct sockaddr *)&dst_addr, sizeof(dst_addr)) != 0)
362             {
363                 DebugLog3("error: connect()\n");
364 #ifdef _WIN32
365                 closesocket(sock);
366                 WSACleanup();
367 #else
368                 close(sock);
369 #endif
370                 return NULL;
371             }
372
373             String *reqmsg = String::string();
374
375             // Request Line
376             String *reqline = String::stringWithFormat("%s %s HTTP/1.1\r\n", request->HTTPMethod()->cString(), url->path()->cString());
377             reqmsg = reqmsg->stringByAppendingString(reqline);
378
379             // headers
380             Dictionary *headers = request->allHTTPHeaderFields();
381             Array *keys = headers->allKeys();
382             for (UInteger i = 0; i < keys->count(); ++i)
383             {
384                 String *key = (String *)keys->objectAtIndex(i);
385                 String *val = headers->stringForKey(key);
386                 String *tmp = String::stringWithFormat("%s: %s\r\n", key->cString(), val->cString());
387                 reqmsg = reqmsg->stringByAppendingString(tmp);
388             }
389
390             reqmsg = reqmsg->stringByAppendingString("\r\n");
391             DebugLog3("requst:\n%s", reqmsg->cString());
392
393 #ifdef _WIN32
394             if (send(sock, reqmsg->cString(), reqmsg->length(), 0) != reqmsg->length())
395 #else
396             if (write(sock, reqmsg->cString(), reqmsg->length()) != reqmsg->length())
397 #endif
398             {
399                 DebugLog3("error: write()\n");
400 #ifdef _WIN32
401                 closesocket(sock);
402 //                WSACleanup();
403 #else
404                 close(sock);
405 #endif
406                 return NULL;
407             }
408
409             DebugLog3("resp wait...\n");
410
411             //
412             HTTPURLResponse *resp = receiveHTTPURLResponse(sock, url);
413             if (resp == NULL)
414             {
415                 DebugLog3("error: receiveHTTPURLResponse()\n");
416 #ifdef _WIN32
417                 closesocket(sock);
418                 WSACleanup();
419 #else
420                 close(sock);
421 #endif
422                 return NULL;
423             }
424
425             if (resp->statusCode() == 200)
426             {
427                 DebugLog3("data wait...\n");
428                 result = receiveData(sock, resp->expectedContentLength());
429                 if (result != NULL)
430                 {
431                     DebugLog3("recv data length: %llu\n", result->length());
432                     if (response != NULL)
433                     {
434                         *response = resp;
435                     }
436                 }
437             }
438
439 #ifdef _WIN32
440             closesocket(sock);
441             WSACleanup();
442 #else
443             close(sock);
444 #endif
445         }
446         else
447         {
448             DebugLog0("not implemented.\n");
449         }
450     }
451     return result;
452 }
453
454 const char *URLConnection::className()
455 {
456     return "URLConnection";
457 }
458
459 } // Raym