/* * kbio_htget.c * BIO API から apr_socket を使う例 * HTTP/1.0 でWebサイトに接続してファイルに保存する。 * HTTP/1.1 にしないのは、チャンク形式に対応していないため。 */ #define USAGE "Usage: kbio_htget " #include "apr_general.h" #include "apr_errno.h" #include "apr_strings.h" #include "apr_uri.h" #include "apr_tables.h" #include "openssl/bio.h" #include "bio_apr_socket.h" #include "mystab.h" #include "myht_util.h" void trim_CRLF(char *line) { int i=0; int len = 0; if (!line) return; len = strlen(line); for (i=len-1; i>=0; i--) { if (line[i] == '\r' || line[i] == '\n') { line[i] = '\0'; } else { break; } } } /* 概要:entity-header と CRLF の読み出し * 返り値: * 成功時:1 * 失敗時:0 最後の CRLF を見つけられなかった時 * メモ:読み出した entity-header は f に渡される * void f (const char *line, void *ctx); */ int BIO_read_entity_headers_CRLF_do ( BIO *in /* 入力ストリーム */ , void *f , void *ctx ) { void (*func)(char *, void *) = f; char line[1024]; while (BIO_gets(in, line, sizeof(line)) >= 0) { trim_CRLF(line); if (line[0] == '\0') { return 1; } /* 取り出した entity-header の処理 */ if (func) (*func)(line, ctx); } return 0; } int apr_my_main ( int ac , char **av , apr_file_t * astdin , apr_file_t * astdout , apr_file_t * astderr , apr_pool_t * pool ) { int error_flag = 0; apr_status_t rv = APR_SUCCESS; char *uri_str = NULL; apr_uri_t uri; char *hostname = NULL; int port = 0; char *out_filename = NULL; BIO *bio = NULL; BIO *bio_out = NULL; apr_table_t * rsp_headers = NULL; if (ac < 3) { apr_file_printf(astderr, "%s\n", USAGE); error_flag = 1; goto _FINALLY_; } uri_str = av[1]; out_filename = av[2]; /* URI のパース */ if (! my_uri_parse(pool, uri_str, &uri)) { apr_file_printf(astderr, "ERROR: uri [%s] ", uri_str); error_flag = 1; goto _FINALLY_; } hostname = uri.hostname; port = uri.port; /* サーバへの接続 */ bio = bio_apr_socket_connect(hostname, port, pool); if (!bio) { apr_file_printf(astderr, "ERROR: bio_apr_socket_connect [%s:%d] ", hostname, port); error_flag = 1; goto _FINALLY_; } /* 出力ファイルのオープン */ bio_out = BIO_new_file(out_filename, "w"); if (!bio_out) { apr_file_printf(astderr, "ERROR: BIO_new_file [%s] ", out_filename); error_flag = 1; goto _FINALLY_; } /* リクエストの送信 */ { int nbytes = 0; char *req = apr_pstrcat(pool, "GET ", uri.path, " HTTP/1.0\r\n", NULL); if ((!strcmp(uri.scheme, "http") && uri.port == 80) || (!strcmp(uri.scheme, "https") && uri.port == 443) ) { req = apr_pstrcat(pool, req, "Host: ", uri.hostname, "\r\n", NULL); } else { req = apr_pstrcat(pool, req, "Host: ", uri.hostname, ":", uri.port_str, "\r\n", NULL); } req = apr_pstrcat(pool, req, "Accept: */*\r\n", NULL); req = apr_pstrcat(pool, req, "User-Agent: khtget/0.1\r\n\r\n", NULL); nbytes = BIO_write(bio, req, strlen(req)); if (nbytes <= 0) { apr_file_printf(astderr, "ERROR: BIO_write "); error_flag = 1; goto _FINALLY_; } } /* バッファリングフィルタ */ { bio = BIO_push(BIO_new(BIO_f_buffer()), bio); if (!bio) { apr_file_printf(astderr, "ERROR: BIO_push "); error_flag = 1; goto _FINALLY_; } } /* レスポンスラインの受信 */ { char rsp_line[1024]; char *rsp_version=NULL; char *status_str=NULL; char *reason_phrase=NULL; int ret = 0; if (BIO_gets(bio, rsp_line, sizeof(rsp_line)) < 0) { error_flag = 1; goto _FINALLY_; } trim_CRLF(rsp_line); ret = read_rsp_line(rsp_line, &rsp_version, &status_str, &reason_phrase, ' '); if (ret < 3) { apr_file_printf(astderr, "ERROR: read_rsp_line "); error_flag = 1; goto _FINALLY_; } apr_file_printf(astdout, "[%s] [%s] [%s]\n", rsp_version, status_str, reason_phrase); } /* レスポンスヘッダの受信 */ rsp_headers = apr_table_make(pool, 10); { int ret = BIO_read_entity_headers_CRLF_do (bio, set_entity_header_to_tab, rsp_headers); if (! ret) { apr_file_printf(astderr, "ERROR: BIO_read_entity_headers_CRLF_do "); error_flag = 1; goto _FINALLY_; } } /* レスポンスヘッダの表示 */ apr_table_do(disp_tab, astdout, rsp_headers, NULL); /* レスポンスボディの受信〜ファイル出力 */ { char buf[1024]; while (1) { int nbytes = BIO_read(bio, buf, sizeof(buf)); if (nbytes <= 0) { break; } nbytes = BIO_write(bio_out, buf, nbytes); if (nbytes < 0) { break; } } } _FINALLY_: if (bio) { BIO_free_all(bio); bio = NULL; } if (bio_out) { BIO_free_all(bio_out); bio_out = NULL; } if (rv != APR_SUCCESS) { char error_buf[80]; apr_file_printf(astderr, "ERROR: %s\n", apr_strerror(rv, error_buf, sizeof(error_buf))); } if (error_flag) { apr_file_printf(astderr, "failed!\n"); return 1; /* 異常終了 */ } apr_file_printf(astdout, "\ndone.\n"); return 0; /* 正常終了 */ } /* end of main */