kmyio_htget3.c
最終更新:2009/12/19
001: /* 002: * kmyio_htget3.c 003: * HTTP/1.1 でWebサイトに接続してファイルに保存する。 004: * SSL 対応。 005: */ 006: 007: #define USAGE "Usage: kmtio_htget3 <uri> <out_filename>" 008: 009: #include "apr_general.h" 010: #include "apr_lib.h" 011: 012: #include "apr_errno.h" 013: #include "apr_strings.h" 014: #include "apr_uri.h" 015: #include "apr_tables.h" 016: 017: #include "openssl/crypto.h" 018: #include "openssl/ssl.h" 019: #include "openssl/err.h" 020: #include "openssl/rand.h" 021: #include "openssl/bio.h" 022: 023: #include "bio_apr_socket.h" 024: #include "mystab.h" 025: #include "myio_chunk.h" 026: #include "myio_apr.h" 027: #include "myht_util.h" 028: #include "myio_bio.h" 029: 030: void * my_alloc(void *alloc_func_ctx, int alloc_size) { 031: apr_pool_t *pool = (apr_pool_t*)alloc_func_ctx; 032: return apr_palloc(pool, alloc_size); 033: } 034: 035: int apr_my_main ( 036: int ac 037: , char **av 038: , apr_file_t * astdin 039: , apr_file_t * astdout 040: , apr_file_t * astderr 041: , apr_pool_t * pool 042: ) { 043: 044: int error_flag = 0; 045: apr_status_t rv = APR_SUCCESS; 046: 047: char *uri_str = NULL; 048: char *out_filename = NULL; 049: 050: apr_uri_t uri; 051: 052: char *hostname = NULL; 053: int port = 0; 054: 055: apr_socket_t *sock = NULL; 056: 057: int ret = 1; 058: 059: int ssl_flag = 0; 060: SSL_CTX *ctx = NULL; 061: BIO *bio = NULL; 062: 063: MYIO *io = NULL; 064: MYIO *in = NULL; 065: MYIO *out = NULL; 066: 067: apr_table_t * rsp_headers = NULL; 068: int rsp_clen = -1; 069: int chunked = 0; 070: 071: if (ac < 3) { 072: apr_file_printf(astderr, "%s\n", USAGE); 073: error_flag = 1; 074: goto _FINALLY_; 075: } 076: 077: uri_str = av[1]; 078: out_filename = av[2]; 079: 080: if (! my_uri_parse(pool, uri_str, &uri)) { 081: apr_file_printf(astderr, "ERROR: uri [%s] ", uri_str); 082: error_flag = 1; 083: goto _FINALLY_; 084: } 085: 086: if (!strcmp(uri.scheme, "https") && uri.port == 443) { 087: ssl_flag = 1; 088: } 089: 090: /* 備忘録:プロキシ使用の拡張の際は接続先をプロキシサーバにする */ 091: hostname = uri.hostname; 092: port = uri.port; 093: 094: /* サーバへの接続 */ 095: sock = my_connect (hostname, port, pool); 096: if (!sock) { 097: apr_file_printf(astderr, "ERROR: connect [%s:%d] ", hostname, port); 098: error_flag = 1; 099: goto _FINALLY_; 100: } 101: 102: /* ソケットをストリームでラップ */ 103: if (ssl_flag) { 104: BIO *sbio = bio_apr_socket_new(sock, pool); 105: if (!sbio) { 106: apr_file_printf(astderr, "ERROR: bio_apr_socket_new "); 107: error_flag = 1; 108: goto _FINALLY_; 109: } 110: 111: /* 備忘録:プロキシ使用の拡張の際はプロキシサーバに CONNECT メソッドの 112: * リクエスト送信し、レスポンスのステータスコードが200かチェックする。 113: */ 114: 115: /* SSL初期化 */ 116: SSL_load_error_strings(); 117: SSL_library_init(); 118: ctx = SSL_CTX_new(SSLv23_client_method()); 119: if (!ctx){ 120: ERR_print_errors_fp(stderr); 121: apr_file_printf(astderr, "ERROR: SSL_CTX_new "); 122: error_flag = 1; 123: goto _FINALLY_; 124: } 125: /* BIO 構造体に SSL コンテクストを保持し、ソケット BIO でチェイン化 */ 126: bio = BIO_push(BIO_new_ssl(ctx, 1), sbio); 127: if (!bio) { 128: apr_file_printf(astderr, "ERROR: BIO_push "); 129: error_flag = 1; 130: goto _FINALLY_; 131: } 132: /* PRNG 初期化 */ 133: RAND_poll(); 134: while ( RAND_status() == 0 ){ 135: unsigned short rand_ret = rand() % 65536; 136: RAND_seed(&rand_ret, sizeof(rand_ret)); 137: } 138: /* SSL で接続 */ 139: ret = BIO_do_connect(bio); 140: if (ret != 1) { 141: ERR_print_errors_fp(stderr); 142: apr_file_printf(astderr, "ERROR: SSL_connect "); 143: error_flag = 1; 144: goto _FINALLY_; 145: } 146: 147: /* MYIO ストリームでラップ */ 148: io = MYIO_bio_new(bio, 1, 1); /* 双方向 */ 149: if (!io) { 150: apr_file_printf(astderr, "ERROR: MYIO_bio_new "); 151: error_flag = 1; 152: goto _FINALLY_; 153: } 154: 155: } else { 156: io = MYIO_sock_new(sock, pool); 157: if (!io) { 158: apr_file_printf(astderr, "ERROR: MYIO_sock_new "); 159: error_flag = 1; 160: goto _FINALLY_; 161: } 162: } 163: 164: /* 出力ファイルのオープン */ 165: 166: out = MYIO_fp_open(out_filename, 167: APR_FOPEN_WRITE|APR_FOPEN_CREATE|APR_FOPEN_BINARY|APR_FOPEN_TRUNCATE, 168: APR_OS_DEFAULT, pool); 169: if (!out) { 170: apr_file_printf(astderr, "ERROR: file open [%s] ", out_filename); 171: error_flag = 1; 172: goto _FINALLY_; 173: } 174: 175: /* リクエストの送信 */ 176: 177: { 178: int nbytes = 0; 179: /* 備忘録:プロキシ使用の拡張時で、SSL でない時はリクエストラインに注意 */ 180: char *req = apr_pstrcat(pool, "GET ", uri.path, " HTTP/1.1\r\n", NULL); 181: if ((!strcmp(uri.scheme, "http") && uri.port == 80) || 182: (!strcmp(uri.scheme, "https") && uri.port == 443) ) { 183: req = apr_pstrcat(pool, req, "Host: ", uri.hostname, "\r\n", NULL); 184: } else { 185: req = apr_pstrcat(pool, req, "Host: ", uri.hostname, ":", uri.port_str, 186: "\r\n", NULL); 187: } 188: req = apr_pstrcat(pool, req, "Accept: */*\r\n", NULL); 189: /* 190: req = apr_pstrcat(pool, req, 191: "Accept-Encoding: gzip, compress, deflate, identity\r\n", NULL); 192: req = apr_pstrcat(pool, req, "Connection: close\r\n", NULL); 193: */ 194: req = apr_pstrcat(pool, req, "User-Agent: khtget/0.1\r\n\r\n", NULL); 195: /* 備忘録:プロキシ使用の拡張時にはプロキシサーバへのヘッダを追加する */ 196: 197: nbytes = MYIO_write(io, req, strlen(req)); 198: if (nbytes <= 0) { 199: apr_file_printf(astderr, "ERROR: MYIO_write "); 200: error_flag = 1; 201: goto _FINALLY_; 202: } 203: } 204: 205: /* バッファリングフィルタ */ 206: { 207: in = MYIO_buf_filter_new(io, 2048, my_alloc, pool); 208: if (!in) { 209: apr_file_printf(astderr, "ERROR: MYIO_buf_filter_new "); 210: error_flag = 1; 211: goto _FINALLY_; 212: } 213: } 214: 215: /* レスポンスラインの受信 */ 216: 217: { 218: char rsp_line[1024]; 219: char *rsp_version=NULL; 220: char *status_str=NULL; 221: char *reason_phrase=NULL; 222: int ret = 0; 223: 224: if (MYIO_gets(in, rsp_line, sizeof(rsp_line)) < 0) { 225: apr_file_printf(astderr, "ERROR: MYIO_gets "); 226: error_flag = 1; 227: goto _FINALLY_; 228: } 229: ret = read_rsp_line(rsp_line, 230: &rsp_version, &status_str, &reason_phrase, ' '); 231: if (ret < 3) { 232: apr_file_printf(astderr, "ERROR: read_rsp_line "); 233: error_flag = 1; 234: goto _FINALLY_; 235: } 236: apr_file_printf(astdout, "[%s] [%s] [%s]\n", 237: rsp_version, status_str, reason_phrase); 238: } 239: 240: /* レスポンスヘッダの受信 */ 241: 242: rsp_headers = apr_table_make(pool, 10); 243: { 244: int ret = MYIO_read_entity_headers_CRLF_do(in, set_entity_header_to_tab, 245: rsp_headers); 246: if (! ret) { 247: apr_file_printf(astderr, "ERROR: MYIO_read_entity_headers_CRLF_do "); 248: error_flag = 1; 249: goto _FINALLY_; 250: } 251: } 252: 253: /* レスポンスヘッダの表示 */ 254: 255: apr_table_do(disp_tab, astdout, rsp_headers, NULL); 256: 257: /* コンテンツレングス、転送エンコーディングのチェック */ 258: { 259: char *tmp = (char*)apr_table_get(rsp_headers, "Content-Length"); 260: if (!tmp) { 261: rsp_clen = -1; 262: } else { 263: rsp_clen = atoi(tmp); 264: } 265: tmp = (char*)apr_table_get(rsp_headers, "Transfer-Encoding"); 266: if (tmp && !strcmp(tmp, "chunked")) { 267: chunked = 1; 268: } 269: } 270: 271: /* レスポンスの受信〜ファイル出力 */ 272: 273: if (chunked) { 274: int nbytes = MYIO_decode_chunked_body(in, out); 275: if (nbytes < 0) { 276: apr_file_printf(astderr, "ERROR: MYIO_decode_chunked_body "); 277: error_flag = 1; 278: goto _FINALLY_; 279: } 280: apr_table_set(rsp_headers, "Content-Length", apr_itoa(pool, nbytes)); 281: rsp_clen = nbytes; 282: 283: apr_table_unset(rsp_headers, "Transfer-Encoding"); 284: chunked = 0; 285: } else { 286: if (rsp_clen > 0) { 287: int nbytes = MYIO_move(in, rsp_clen, out); 288: if (nbytes != rsp_clen) { 289: apr_file_printf(astderr, "ERROR: MYIO_move "); 290: error_flag = 1; 291: goto _FINALLY_; 292: } 293: } 294: } 295: 296: _FINALLY_: 297: 298: if (out) { 299: MYIO_fp_close(out); 300: out = NULL; 301: } 302: 303: if (io) { 304: 305: if (ssl_flag) { 306: if (ctx) { 307: SSL_CTX_free(ctx); 308: ctx = NULL; 309: } 310: if (bio) { 311: BIO_free_all(bio); 312: bio = NULL; 313: } 314: MYIO_bio_close(io); 315: 316: ssl_flag = 0; 317: } else { 318: MYIO_sock_close(io); 319: sock = NULL; 320: } 321: 322: io = NULL; 323: } 324: 325: if (sock) { 326: apr_socket_close(sock); 327: sock = NULL; 328: } 329: 330: 331: if (in) { 332: MYIO_buf_free(in, NULL, NULL); 333: in = NULL; 334: } 335: 336: if (rv != APR_SUCCESS) { 337: char error_buf[80]; 338: apr_file_printf(astderr, "ERROR: %s\n", 339: apr_strerror(rv, error_buf, sizeof(error_buf))); 340: } 341: 342: if (error_flag) { 343: apr_file_printf(astderr, "failed!\n"); 344: return 1; /* 異常終了 */ 345: } 346: 347: apr_file_printf(astdout, "\ndone.\n"); 348: 349: return 0; /* 正常終了 */ 350: 351: } /* end of main */
KAKU PROJECT (2009) |