9 #include <sys/socket.h>
16 #include <Raym/URLConnection.h>
17 #include <Raym/HTTPURLResponse.h>
22 URLConnection::URLConnection()
26 URLConnection::~URLConnection()
31 // socketからterminatorで指定する終端文字列に遭遇するまで読み込む。
32 // 返却したバッファは必ずfreeすること。
36 static char *readLine(SOCKET socket, const char *terminator)
38 static char *readLine(int socket, const char *terminator)
42 if ((socket >= 0) && (terminator != NULL) && (strlen(terminator) > 0))
44 static const int BUF_UNIT = 256;
45 long long unit_count = 0;
46 long long term_len = strlen(terminator);
52 if (offset == (unit_count * BUF_UNIT))
55 char *newbuf = (char *)realloc(buf, (unit_count * BUF_UNIT));
62 printf("realloc ng.\n");
76 int len = recv(socket, &buf[offset], 1, 0);
86 printf("recv error.\n");
90 if (offset >= (term_len - 1))
92 if (memcmp(&buf[offset - term_len + 1], terminator, term_len) == 0)
104 static HTTPURLResponse *receiveHTTPURLResponse(SOCKET socket, URL *url)
106 static HTTPURLResponse *receiveHTTPURLResponse(int socket, URL *url)
109 HTTPURLResponse *result = NULL;
117 statusLine = readLine(socket, "\r\n");
118 if (statusLine == NULL)
120 DebugLog3("error: read status line.\n");
123 if (strlen(statusLine) > 0)
128 DebugLog3("error: read status line. (retry)\n");
133 char *p = strchr(statusLine, ' ');
134 if ((p == NULL) || ((strncmp("HTTP/1.0 ", statusLine, 9) != 0) && (strncmp("HTTP/1.1 ", statusLine, 9) != 0)))
136 DebugLog3("error: invalid format. (HTTP-Version) \"%s\"\n", statusLine);
142 String *http_version = String::stringWithUTF8String(statusLine);
145 p = strchr(code, ' ');
148 DebugLog3("error: invalid format. (Status-Code) \"%s\"\n", statusLine);
154 int status_code = atoi(code);
156 sprintf_s(tmp, sizeof(tmp), "%d", status_code);
157 if (strcmp(code, tmp) != 0)
159 DebugLog3("error: invalid format. (Status-Code) \"%s\"\n", statusLine);
164 char *reason = p + 1;
165 p = strstr(reason, "\r\n");
168 DebugLog3("error: invalid format. (Reason-Phrase) \"%s\"\n", statusLine);
174 String *reason_phrase = String::stringWithUTF8String(reason);
178 DebugLog3("status line: %s %d %s\n", http_version->cString(), status_code, reason_phrase->cString());
180 Dictionary *headers = Dictionary::dictionaryWithCapacity(0);
182 if (http_version->isEqualToString("HTTP/1.1"))
187 char *header_line = readLine(socket, "\r\n");
188 if (header_line == NULL)
192 if (strcmp("\r\n", header_line) == 0)
198 char *p = strchr(header_line, ':');
203 while (*value == ' ')
207 p = strstr(value, "\r\n");
209 headers->setString(value, header_line);
210 DebugLog3("%s: %s\n", header_line, value);
219 result = HTTPURLResponse::alloc()->initWithURL(url, status_code, http_version, headers);
222 result->autorelease();
229 static Data *receiveData(SOCKET socket, long long expectedContentLength)
231 static Data *receiveData(int socket, long long expectedContentLength)
234 DebugLog2("receiveData(%d, %lld)\n", socket, expectedContentLength);
241 static const int BUF_UNIT = 1024;
242 long long unit_count = 0;
244 long long offset = 0;
248 if (offset == (unit_count * BUF_UNIT))
251 char *newbuf = (char *)realloc(buf, (unit_count * BUF_UNIT));
266 if (expectedContentLength == URLResponseUnknownLength)
270 else if ((expectedContentLength - offset) > BUF_UNIT)
276 rdlen = (expectedContentLength - offset);
278 size_t len = recv(socket, &buf[offset], rdlen, 0);
287 if (expectedContentLength != URLResponseUnknownLength)
294 if (offset >= expectedContentLength)
302 result = Data::alloc()->initWithBytesAndLength(buf, offset);
306 result->autorelease();
313 Data *URLConnection::sendSynchronousRequest(URLRequest *request, URLResponse **response, Error **error)
315 DebugLog2("URLConnection::sendSynchronousRequest()\n");
318 // 現状は、更なるクラス群を調査する気力が無いので
320 // とりあえず linux socket で実装。あとでwinsockに対応
326 URL *url = request->url();
327 if (url->scheme()->isEqualToString("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());
336 DebugLog3("error: gethostbyname().\n");
340 dst_addr.sin_addr.s_addr = *(unsigned int *)hostent->h_addr;
342 dst_addr.sin_addr.s_addr = *(uint32_t *)hostent->h_addr;
347 // WSAStartup(MAKEWORD(2,0), &wsaData);
348 SOCKET sock = INVALID_SOCKET;
349 sock = socket(AF_INET, SOCK_STREAM, 0);
350 if (sock == INVALID_SOCKET)
353 sock = socket(AF_INET, SOCK_STREAM, 0);
357 DebugLog3("error: socket()\n");
361 if (connect(sock, (const struct sockaddr *)&dst_addr, sizeof(dst_addr)) != 0)
363 DebugLog3("error: connect()\n");
373 String *reqmsg = String::string();
376 String *reqline = String::stringWithFormat("%s %s HTTP/1.1\r\n", request->HTTPMethod()->cString(), url->path()->cString());
377 reqmsg = reqmsg->stringByAppendingString(reqline);
380 Dictionary *headers = request->allHTTPHeaderFields();
381 Array *keys = headers->allKeys();
382 for (UInteger i = 0; i < keys->count(); ++i)
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);
390 reqmsg = reqmsg->stringByAppendingString("\r\n");
391 DebugLog3("requst:\n%s", reqmsg->cString());
394 if (send(sock, reqmsg->cString(), reqmsg->length(), 0) != reqmsg->length())
396 if (write(sock, reqmsg->cString(), reqmsg->length()) != reqmsg->length())
399 DebugLog3("error: write()\n");
409 DebugLog3("resp wait...\n");
412 HTTPURLResponse *resp = receiveHTTPURLResponse(sock, url);
415 DebugLog3("error: receiveHTTPURLResponse()\n");
425 if (resp->statusCode() == 200)
427 DebugLog3("data wait...\n");
428 result = receiveData(sock, resp->expectedContentLength());
431 DebugLog3("recv data length: %llu\n", result->length());
432 if (response != NULL)
448 DebugLog0("not implemented.\n");
454 const char *URLConnection::className()
456 return "URLConnection";