kmyio_htget3.c

最終更新:2009/12/19

kmyio_htget3.c

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 */
Copyright (C) KAKU PROJECT (2009)KAKU PROJECT (2009)