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) |