0、简介
1、定义
RFC定义 https://tools.ietf.org/html/rfc2616#section-3.6.1
Chunked-Body = *chunk last-chunk trailer CRLF chunk = chunk-size [ chunk-extension ] CRLF chunk-data CRLF chunk-size = 1*HEX last-chunk = 1*("0") [ chunk-extension ] CRLF chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) chunk-ext-name = token chunk-ext-val = token | quoted-string chunk-data = chunk-size(OCTET) trailer = *(entity-header CRLF)
2.1、Entity Header Fields
https://tools.ietf.org/html/rfc2616#section-7.1
2、解析
解码伪代码 https://tools.ietf.org/html/rfc2616#section-19.4.6
length := 0 //body总长度初始化为0 read chunk-size, chunk-extension (if any) and CRLF //读取第一行 获取 第一块 chunked 数据的大小(chunk扩展项) while (chunk-size > 0) { read chunk-data and CRLF //读取chunk-data, chunk-data 的长度为 chunk-size, 后面跟 表示结束, chunk-size不包含 append chunk-data to entity-body //将chunk-data 追加到 实体body 中(解码后) length := length + chunk-size //body总长度更新 read chunk-size and CRLF //读取下一个 chunk头 获取chunk-size }
//退出循环说明 chunk-size 为0, 即last-chunk, last-chunk后面可能会跟有trailer read entity-header //读取 entity-header while (entity-header not empty) { //读到空行,即整行内容只是 这两个字节 append entity-header to existing header fields read entity-header } Content-Length := length Remove "chunked" from Transfer-Encoding
运行方式 ./a.out -u http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx -t target.jpg
#define _GNU_SOURCE /* for memmem */ #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> /* chunked-encoding example URL GET http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx */ void parse_paramters(int argc, char* argv[], char** url, char** target); void parse_req_url(char* url, char** host, char** service, char** uri, char** target); /* * host can be domain-name or ip-address * service can be well-known service name("http"/"ftp") or port number */ int connect_to_server(char* host, char* service); void send_req_to_server(int fd, char* uri, char* host, char* service); void recv_res_from_server(int fd, char* store_path); int main(int argc, char* argv[]) { int sfd; /* socket file descriptor */ char *url, *host, *service, *uri, *target = NULL; /* parse request */ parse_paramters(argc, argv, &url, &target); parse_req_url(url, &host, &service, &uri, target ? NULL : &target); printf("Host : [%s] ", host); printf("Port : [%s] ", service); printf("Uri : [%s] ", uri); printf("Target : [%s] ", target); /* create the connection to server */ sfd = connect_to_server(host, service); /* send http req to server */ send_req_to_server(sfd, uri, host, service); free(uri); free(host); free(service); /* get response from server */ recv_res_from_server(sfd, target); free(target); /* cleanup */ shutdown(sfd, SHUT_RDWR); close(sfd); exit(EXIT_SUCCESS); } void parse_paramters(int argc, char* argv[], char** url, char** target) { int opt; if (!(url && target && (argc > 1) && argv)) { fprintf(stderr, "Usage: %s [-u url] [-t store_path] ", argv[0]); exit(EXIT_FAILURE); } while ((opt = getopt(argc, argv, "u:t:")) != -1) { switch (opt) { case 'u': *url = optarg; break; case 't': *target = strdup(optarg); break; default: /* '?' */ fprintf(stderr, "Usage: %s [-u url] [-t store_path] ", argv[0]); exit(EXIT_FAILURE); } } } void parse_req_url(char* url, char** host, char** service, char** uri, char** target) { char* tmp; char* token; /* skip scheme */ token = strstr(url, "://"); if (token) { url = token + sizeof("://") - 1; } /* find uri */ token = strchr(url, '/'); if (NULL == token) { *uri = strdup("/"); if (target) { *target = strdup("index.html"); } } else { *uri = strdup(token); *token = '