HTTP Client の動作フロー その9 : useragent.c
今まで勉強したことのまとめとして、指定ホストのトップページをGETで読み込み、HTTPレスポンスを標準出力に表示するツールをつくってみました。
/** * useragent.c **/ #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h> #include <string.h> #include <arpa/inet.h> #include <unistd.h> #define BUF_LEN 1024 int main(int argc, char *argv[]) { struct addrinfo hints; struct addrinfo *result, *rp; int error, sfd; char *host, *err_mess, buf[BUF_LEN]; size_t len; ssize_t n; if (argc < 2) { fprintf(stderr, "Usage: %s hostname\n", argv[0]); return 1; } //host情報を代入 host = argv[1]; //hintsを初期化 memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; //IPv4とIPv6どちらでもOK hints.ai_socktype = SOCK_STREAM; //TCP hints.ai_flags = 0; hints.ai_protocol = 0; //Any protocol //名前解決 error = getaddrinfo(host, "http", &hints, &result); //エラー処理 if (error != 0) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); return 1; } //ホストに接続 for (rp = result; rp != NULL; rp = rp->ai_next) { //ソケットを生成 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); //ソケットエラー処理 if (sfd == -1) { //ソケットが生成できなかった場合,次のアドレスでリトライ err_mess = "socket"; continue; } //ホストに接続 if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) { //接続できた場合はループを抜ける break; } err_mess = "connect"; //接続できかなった場合はソケットを閉じて次のアドレスでリトライ close(sfd); } //すべてのアドレスで接続に成功しなかった場合は終了 if (rp == NULL) { fprintf(stderr, "error : %s\n", err_mess); return 1; } //resultのメモリ領域を開放 freeaddrinfo(result); //送信内容を作成 snprintf(buf, sizeof(buf), "GET / HTTP/1.0\r\nHost: %s\r\nConnection: close\r\n\r\n", host); //リクエストを送信 len = strlen(buf); error = write(sfd, buf, len); if(error < len) { fprintf(stderr, "error : HTTP"); close(sfd); return 1; } //バッファを空に memset(buf, 0, sizeof(buf)); //レスポンスを受信して出力 do { //バッファ分読み込む n = read(sfd, buf, sizeof(buf)); //標準出力に表示 if(write(STDOUT_FILENO, buf, n) < 0) { perror("write STDOUT"); return 1; } } while(n > 0); //エラー処理 if(n == -1) { perror(""); return 1; } close(sfd); return 0; }
使用方法
引数で与えられたホストのトップページをGETで読み込み、HTTPレスポンスを標準出力に表示。
$ useragent localhost HTTP/1.1 200 OK Date: Sat, 28 May 2011 21:31:04 GMT Server: Apache/2.2.17 (Ubuntu) Last-Modified: Sat, 09 Apr 2011 05:55:14 GMT ETag: "42154-b1-4a075fc77fa69" Accept-Ranges: bytes Content-Length: 177 Vary: Accept-Encoding Connection: close Content-Type: text/html <html><body><h1>It works!</h1> <p>This is the default web page for this server.</p> <p>The web server software is running but no content has been added, yet.</p> </body></html>
TODO
- URLを指定できるようにする
- 例:http://example.com:8888/hoge/fuge.html
- scheme,host,domain,port,path,datanameなどにURLを分割する必要がある
- ファイルとして保存できるようにする
- 処理をサブルーチンに分割
- リクエストとレスポンスのヘッダー情報を構造体として取り扱えるようにする