http.c

さて、ここらでようやくhttp.cです。
こいつの主要な機能は

 ① httpメッセージの作成補助および作成
 ② 適切なポートへのtcp(over ssl)接続の確立

です。
指定されたホスト、ポートへの接続は、connect.cに任せているのですが、
こいつでhttpだろうとhttpsだろうと同じように扱えるように作ってみました。

なお、ver.2ができましたので、ソースはそちらをどうぞ。
① httpメッセージの作成補助および作成
twitter.c ー ①−4の最後に乗ってるみたいなのが、HTTPメッセージです。
最初の一行がリクエスト行、
そのご空行までがヘッダ、
POSTの場合はその空行のあとにメッセージボディをいれます。
というか、メッセージボディなしでリクエストするときに使うのがGET、
ありでリクエストするときに使うのがPOSTです。

というわけで、そのメッセージの作成のため、それぞれに
int http_mk_reqline(char *s, int GorP, char *pass)
int http_mk_field(char *s, char *name, char *value)
int http_mk_content(char *s, char *content)
と、関数を用意しました。
やってることはchar *sからうまいこと書きこんで、書き込んだ文字数を返すだけの文字列処理です。

また、ただGETしたいだけ、というときのために、上の3つを使って、
int http_mk_simpleget(char *msg, char *url);
も作りました。char *msgにうまいことメッセージを作ります。
② 適切なポートへのtcp(over ssl)接続の確立
ちょいとここらで何もなしに説明するのがつらいので、ソースを載せます。
#ifndef _HTTP
#define _HTTP

#include <stdio.h>
#include <string.h>

#include "file.c"
#include "connect.c"
#include "wait.c"
#include "url.c"

#define HTTP_PORT 80
#define HTTPS_PORT 443

int http_mk_reqline(char *s, int GorP, char *pass);
int http_mk_field(char *s, char *name, char *value);
int http_mk_content(char *s, char *content);
int http_mk_simpleget(char *msg, char *url);

int http_send(int *sock, SSL **ssl, char *url, char *msg, int msize);
int http_recv_header(int sock, SSL *ssl, char *head, int hsize);
int http_recv_all(int sock, SSL *ssl, char *all, int asize);

int http_msgget(char *enurl, char *msg, int msize, char *data, int dmax);
int http_get(char *url, char *data, int dmax);
int http_getsave(char *url, char *name);


int http_mk_reqline(char *s, int GorP, char *pass){
   static char *type[2] = {"GET", "POST"};
   return sprintf(s, "%s %s HTTP/1.1\r\n", type[GorP], pass);
}

// make field at s
int http_mk_field(char *s, char *name, char *value){
   return sprintf(s, "%s: %s\r\n", name, value);
}

// make content at s(end of header)
int http_mk_content(char *s, char *content){
   return sprintf(s, "\r\n%s\r\n", content);
}

int http_mk_simpleget(char *msg, char *url){
   int msize = 0;
   static char host[100], pass[100];
   url_parse(url, NULL, NULL, host, pass, NULL);
   msize += http_mk_reqline(msg, 0, pass);
   msize += http_mk_field(msg + msize, "Host", host);
   msize += http_mk_field(msg + msize, "Accpet", "*/*");
   msize += http_mk_content(msg + msize, "");
   return msize;
}

// send get request and get sockint, ssl. return byte
int http_send(int *sock, SSL **ssl, char *url, char *msg, int msize){
// connect
   char prot[6];
   static char host[100];
   url_parse(url, prot, NULL, host, NULL, NULL);
   int isssl = (strcmp(prot, "https") == 0);
   if(con_connect(host, (isssl) ? HTTPS_PORT : HTTP_PORT, isssl, sock, ssl) == 0) return 0;

// send message
   SSL *tmpssl = (ssl == NULL) ? NULL : *ssl;
   return con_send(*sock, tmpssl, msg, msize);
}

// recv header
int http_recv_header(int sock, SSL *ssl, char *head, int hsize){
   char vain[5000];
   int enter = 0, flag = 0;
   if(head == NULL){
      head = vain;
      hsize = 5000;
   }
   while(1){
      if(con_recv(sock, ssl, head, 1) <= 0) return 0;
      if(--hsize == 0) return 0;
      if(*head == '\n'){
   if(enter == 1) flag = 1;
   else enter = 1;
      }
      else if(*head != '\r') enter = 0;
      head++;
      if(flag) return 1;
   }
}

// recv all and close socket. return size
int http_recv_all(int sock, SSL *ssl, char *all, int asize){
   int size = 0, tmp;
   if(ssl != NULL) sock = SSL_get_fd(ssl);
   while(wait(sock, 6000, 0) > 0){
      tmp = con_recv(sock, ssl, all + size, asize - size);
      if(tmp <= 0){
   printf("finish(%d)\n", tmp);
   break;
      }
      size += tmp;
      printf("...%d\n", size);
   }
   con_close(sock, ssl);
   return size;
}

int http_msgget(char *enurl, char *msg, int msize, char *data, int dmax){
// send message
   int s;
   SSL *ssl;
   if(http_send(&s, &ssl, enurl, msg, msize) == 0) return 0;

// recv through header
   http_recv_header(s, ssl, NULL, 0);

// recv content and close socket
   return http_recv_all(s, ssl, data, dmax);   
}

// encode url and get, input to *data. return filesize
int http_get(char *url, char *data, int dmax){
// make message
   char msg[5000];
   char *enurl = url_encode_url(url, strlen(url));
   int msize = http_mk_simpleget(msg, enurl);

   int re = http_msgget(enurl, msg, msize, data, dmax);
   free(enurl);
   return re;
}

// get(<100MB) and save to name
int http_getsave(char *url, char *name){
// if name is directory, get name from url
   char filename[500];
   strcpy(filename, name);
   if(name[strlen(name) - 1] == '/'){
      char *up = url + strlen(url) - 1;
      while(*(--up) != '/');
      char *fnp = filename + strlen(name);
      while((*(fnp++) = *(++up)) != '\0');
   }
// get
   static char data[100000000];
   int size = http_get(url, data, 100000000);
   
// save
   file_puts(filename, data, size);
   printf("save as %s\n", filename);
   return size;
}

#endif
まず基本的な送受信をするのが、
http_send, http_recv_header, http_recv_all
の3つです。

http_sendで、urlに応じてソケットの作成、httpsの場合はSSL *の作成をした後、メッセージを送信します。
url.cconnect.cを使っているので、この関数自体は単純です。

http_recv_headerでは、connect.cのcon_recv関数を使い、一文字ずつ読み取っています。
\r\nが改行のはずですが、一応\nのみでも改行として扱えるようにしています。

http_recv_allでは、受信出来るだけ全部受信し、connectionを閉じます。
wait.cのwait関数をつかって、6秒受信できなければタイムアウトするようになっています。


そしてこれらを使って、sendしrecvし、データをもらう関数が、残りの
http_msgget, http_get, http_getsave 関数です。
メッセージを自前で作って、それを送ってデータを受信するのがhttp_msgget、
もうurlだけ与えてかってにメッセージ作って送信して受信してもらうのがhttp_get、
さらにそうしてgetしたものを与えられたパスに保存するのが、http_getsaveです。
ほんの少し保存するのがめんどくさいので、file.cも使ってます。

コメントをお書きください

コメント: 2
  • #1

    funcHM (月曜日, 17 2月 2014 22:39)

    自分のハンドルネームとこのブログ名が似ていたので縁を感じて訪問しました。

    僕も今Javaでhttpクライアント書いているんです(笑)

    それだけです~。
    ではまたきます。

  • #2

    tororo7 (火曜日, 18 2月 2014 01:27)

    あれま、コメントありがとうございます。

    しばらく更新しないこともありますが、気長にやっていく予定ですので、末永くよろしくおねがいします。(´ー`)