📄 http.c
字号:
#include <strings.h>#include <stdio.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <sys/epoll.h>#include <unistd.h>#include <netdb.h>#include <errno.h>#include <stdlib.h>#include <arpa/inet.h>#include <pthread.h>#include "http.h"#include "function_restart.h"char *base_name (const char *);extern struct resource_info resource;extern struct file_range *files_range;extern struct command cmd;extern pthread_mutex_t mutex_cmd;extern pthread_cond_t cond_cmd_set;extern pthread_cond_t cond_cmd_clear;extern int pipe_recv_send[2];int recv_response_head_file (int, void *const, size_t);void retry (int, struct file_range * const);void update_file_range (struct file_range *, size_t);void close_connect (const int, struct file_range *const);void close_connects (int);size_t c_ntolower (char *const, size_t);inline char c_tolower (char *const);struct file_range *get_new_connect (int);struct file_range *fd_to_file_range (int);int safe_http_code (const char);int print_result (size_t);void print_resource_info (void);void print_resource_size (void);void print_size_human (size_t);void clear_cmd (struct command *const);void send_cmd (const struct command *);void wait_cmd (struct command *const);void epoll_remove (int, int);int epoll_add (int, int);inline int readable (struct epoll_event *);int parse_url_http (struct url_http *const, char *const);void clear_file_range (struct file_range *);char *base_name (const char *path){ char *base; return (char *)((base = strrchr (path, '/'))? (base + 1): path);}int parse_url_http (struct url_http *const uh, char *const url){ size_t len_url; size_t len_scheme; char *scheme_sfix; char *domain; char *port; char *path; /* is scheme http ? */ scheme_sfix = strstr (url, "://"); len_scheme = scheme_sfix - url; while (0 != len_scheme) { if (LEN_SCHEME_HTTP == len_scheme) { c_ntolower (url, LEN_SCHEME_HTTP); if (0 == strncmp (url, SCHEME_HTTP, LEN_SCHEME_HTTP)) break; } fprintf (stderr, "invalid protocol.\n"); return -1; } /* is the length of url avalid ? */ len_url = strlen (url) - len_scheme - sizeof ("://") + 1; if (MIN_LEN_URL_HTTP > len_url || MAX_LEN_URL_HTTP < len_url) { fprintf (stderr, "invalid length of URL.\n"); return -1; } domain = url + len_scheme; domain += (0 != len_scheme)? (sizeof ("://") - 1): 0; (*uh).domain = domain; port = strchr (domain, ':'); path = strchr (domain, '/'); if (NULL != ((*uh).path = path)) { (*uh).port = port + 1; if ((NULL == port) || (port > path)) /* port is empty. */ (*uh).len_port = 0; else /* port is't empty */ (*uh).len_port = (path - (port + 1)); if ((MIN_LEN_PORT > (*uh).len_port) || (MAX_LEN_PORT < (*uh).len_port)) { fprintf (stderr, "the length of URL port is invalid.\n"); return -1; } (*uh).len_path = strlen (path); if (MIN_LEN_PATH > (*uh).len_path || MAX_LEN_PATH < (*uh).len_path) goto __FUNCTION__invalid_path; (*uh).len_domain = (len_url - (*uh).len_port - (*uh).len_path); if (0 != (*uh).len_port) (*uh).len_domain--; if ((MIN_LEN_DOMAIN > (*uh).len_domain) || (MAX_LEN_DOMAIN < (*uh).len_domain)) { fprintf (stderr, "the length of URL domain is invalid.\n"); return -1; } return 0; } __FUNCTION__invalid_path: fprintf (stderr, "the length of URL path is invalid.\n"); return -1;}int get_resource_size (char *const buf){ char content_length[10]; long int size; int retval = 0; char *p; char *terminal; unsigned char i = 0; int errno_old = errno; if ((NULL == (p = strstr (buf, CONTENT_LENGTH)))) return -1; p += LEN_CONTENT_LENGTH; terminal = strstr(p, HEAD_TERMINAL); while (C_ISBLANK (*p)) p++; if (':' != *p) goto __FUNCTION__error; p++; while (C_ISBLANK (*p)) p++; while (C_ISNUM (*p) && i < (sizeof (content_length) - 1)) { content_length[i] = *p; i++; p++; } while (!C_ISBLANK (*p)) p++; if (p < terminal) goto __FUNCTION__error; content_length[i] = '\0'; errno_old = errno; errno = 0; size = strtol (content_length, NULL, 10); if (0 != errno) goto __FUNCTION__error; resource.size = (size_t) size; retval = 0; __FUNCTION__error: errno = errno_old; return retval;}int safe_http_code (const char c){ if (C_ISNUM (c)) return 1; if (C_ISALPHA (c)) return 1; switch (c) { case '%': case '/': case '.': case '#': case '?': case ';': case ':': case '-': return 1; default: return 0; }}int get_redirect_url (char *const buf, char *const url, const size_t size_url){ char *p; char *p_remain; size_t i = 0; if ((NULL == url) || (0 == size_url)) { fprintf (stderr, "no room for URL.\n"); return -1; } *(url + size_url - 1) = '\0'; p = strstr (buf, CONTENT_PFIX); if (NULL == p) goto __FUNCTION__invalid_url; p += LEN_CONTENT_PFIX; while (C_ISBLANK (*p)) p++; if (':' != *p) goto __FUNCTION__invalid_url; p++; while (C_ISBLANK (*p)) p++; p_remain = url; while (safe_http_code (*p) && i < (size_url - 1)) { *p_remain = *p; p_remain++; p++; i++; } if (safe_http_code (*p)) { fprintf (stderr, "redirect URL it too long.\n"); return -1; } while (C_ISBLANK (*p)) p++; if (*p == '\r') { *p_remain = '\0'; return 0; } __FUNCTION__invalid_url: fprintf (stderr, "redirect URL is invalid.\n"); return -1;}int strtoshort (char *const s, short *size){ int errno_old; long int tosize; int retval = -1; char *end; errno_old = errno; errno = 0; tosize = strtol (s, &end, 10); if ((0 != errno) || ('\0' != *end)) goto __FUNCTION__error; if ((0 >= tosize) || ((MAX_PORT < tosize))) goto __FUNCTION__error; *size = tosize; retval = 0; __FUNCTION__error: errno = errno_old; return retval;}int setup_sockaddr (struct sockaddr_in *const sock, struct url_http * const uh){ struct hostent *host; short p; char domain[MAX_LEN_DOMAIN + 1]; char port[MAX_LEN_PORT + 1]; strncpy (port, (*uh).port, (*uh).len_port); if (0 == (*uh).len_port) p = 80; else if (((*uh).len_port > MAX_LEN_PORT) || (-1 == strtoshort (port, &p))) { fprintf (stderr, "invalid server port.\n"); return -1; } strncpy (domain, (*uh).domain, (*uh).len_domain); *(domain + (*uh).len_domain) = '\0'; if (NULL == (host = gethostbyname (domain))) { fprintf (stderr, "%s.\n", hstrerror (h_errno)); fprintf (stderr, "can't get server address.\n"); return -1; } bzero (sock, SIZE_SOCKADDR); (*sock).sin_family = (*host).h_addrtype; (*sock).sin_port = htons (p); (*sock).sin_addr = *((struct in_addr *) host->h_addr); return 0;}int send_request_head_file (struct sockaddr_in *const sockaddr, \ struct url_http *const uh){ int fd_sock; int sockopt; if (-1 == (fd_sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP))) { fprintf (stderr, "create socket failure.\n"); return -1; } if (-1 == connect (fd_sock, (struct sockaddr *) sockaddr, SIZE_SOCKADDR)) { close_restart (fd_sock); fprintf (stderr, "connect to server failure.\n"); return -1; } if (-1 == send_restart (fd_sock, "HEAD ", (sizeof ("HEAD ") - 1), MSG_MORE)) goto __FUNCTION__SEND_FAILURE; if (-1 == send_restart (fd_sock, (*uh).path, (*uh).len_path, MSG_MORE)) goto __FUNCTION__SEND_FAILURE; if (-1 == send_restart (fd_sock, " HTTP/1.0\r\n\r\n", 13, 0)) goto __FUNCTION__SEND_FAILURE; sockopt = 1; setsockopt (fd_sock, SOL_SOCKET, TCP_NODELAY, (void *) &sockopt, SIZE_INT); return fd_sock; __FUNCTION__SEND_FAILURE: fprintf (stderr, "send request to server failure.\n"); close_restart (fd_sock); return -1;}int recv_response_head_file (int fd_sock, void *const buf, size_t size_buf){ ssize_t nbytes_recv; void *remain; void *next; size_t size_remain; char *p; int search = 0; remain = buf; next = remain; size_remain = size_buf - 2; *(char *)(buf + size_remain + 1) = '\0'; while (1) { if (-1 != (nbytes_recv = recv (fd_sock, remain, size_remain, 0))) { remain += nbytes_recv; size_remain -= nbytes_recv; *(char *) remain = '\0'; if (0 == search) { if (remain - buf < LEN_CONTENT_PFIX) continue; search = 1; } if (NULL != (p = strstr (next, CONTENT_PFIX))) { *(p + LEN_CONTENT_PFIX) = '\0'; return 0; } if (0 == size_remain) break; next = remain - LEN_CONTENT_PFIX; } else if (EINTR == errno) continue; else break; } fprintf (stderr, "Response is invailable.\n"); return -1;}int get_resource_info (char *const location){ struct url_http uh; char *buf; const size_t size_buf = 2048; char *url; struct sockaddr_in sockaddr_server; char *p; int fd_sock; int retval = -1; unsigned long int redirect = 0; /* alloc memory. */ if (NULL == (buf = (char *) calloc (1, size_buf))) { fprintf (stderr, "Alloc memory failure.\n"); goto __FUNCTION__return; } url = location; /* get the size and location of resource. */__FUNCTION__redirect: if (-1 == parse_url_http (&uh, url)) return -1; if (-1 == setup_sockaddr (&sockaddr_server, &uh)) goto __FUNCTION__free_buffer; if (-1 == (fd_sock = send_request_head_file (&sockaddr_server, &uh))) goto __FUNCTION__free_buffer; if (-1 == recv_response_head_file (fd_sock, buf, size_buf)) goto __FUNCTION__close_fd_connect; *(buf + size_buf - 1) = '\0'; if ((0 == strncmp (buf, RES_OK_1_0, LEN_HTTP_RES)) || \ (0 == strncmp (buf, RES_OK_1_1, LEN_HTTP_RES))) { if (-1 == get_resource_size (buf)) goto __FUNCTION__close_fd_connect; resource.sockaddr.sin_family = sockaddr_server.sin_family; resource.sockaddr.sin_port = sockaddr_server.sin_port; resource.sockaddr.sin_addr = sockaddr_server.sin_addr; strncpy (resource.path, uh.path, uh.len_path); *(resource.path + uh.len_path) = '\0'; resource.len_path = uh.len_path; retval = 0; goto __FUNCTION__close_fd_connect; } else if ((0 == strncmp (buf, RES_RED_1_0, LEN_HTTP_RES)) || \ (0 == strncmp (buf, RES_RED_1_1, LEN_HTTP_RES))) { redirect++; if (0 == redirect) { if (NULL == (url = calloc (1, (MAX_LEN_URL_HTTP + 8)))) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -