http.c ー ver.2

http.cを使ってて思ったのですが、ファイル送信終了をお知らせしてくれない時には6秒のタイムアウトを待つしかなく、なかなか不便です。
というわけで

① http_getsize_fromheader 関数を作り、headerを受け取って、そこからContent-Lengthフィールドを探しだしデータの大きさを返す。
① http_msggetの中身を変え、headerを受信し、上の関数でデータの大きさをGETし、それをhttp_recv_allに渡す。
② http_recv_allで、目的のサイズ受信したら終了する処理を加える

ことにより、ちと便利になりました。
しかしなかなかごっちゃかめっちゃかで、もうちと綺麗なプログラムにしたいものです。

ひとまずver.2のソースを下に載せます。
#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_getsize_fromheader(char *header);
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){
   *head = '\0';
   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);
      if(size == asize){
   printf("all received(%d)\n", size);
   break;
      }
   }
   con_close(sock, ssl);
   return size;
}

// get data size from header
int http_getsize_fromheader(char *header){
// find Content-Length field and read value
   char *h = strstr(header, "Content-Length: ");
   char cl[20];
   char *clp = cl;
   int clen = 0;
   if(h != NULL){
      h += strlen("Content-Length: ");
      while('0' <= *h && *h <= '9') *(clp++) = *(h++);
      *clp = '\0';
      sscanf(cl, "%d", &clen);
      printf("Content-Length is %d\n", clen);
      return clen;
   }
   else return -1;
}

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 header
   static char header[5000];
   http_recv_header(s, ssl, header, 5000);
// get data size
   int clen = http_getsize_fromheader(header);
   if(clen > dmax) return 0;
// recv content and close socket
   return http_recv_all(s, ssl, data, (clen == -1) ? dmax : clen);
}

// 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.cを使うと、
下のように簡単に画像をダウンロード・セーブできます。
#include "http.c"
int main(){
   http_getsave("http://u.jimdo.com/www59/o/se2f43d301ffa1c2e/img/i8b8c3bdbc7c851f6/1388933572/std/image.png", "./");
   return 0;
}

$ ./test
connect to ...
Name : u.jimdo.com
  IP : 14.0.38.72
connection OK !
Content-Length is 7676
...1402
...2804
...7676
all received(7676)
save as ./image.png