├── .gitignore ├── src ├── config ├── mod_fastdfs.conf ├── common.h ├── ngx_http_fastdfs_module.c └── common.c ├── INSTALL ├── HISTORY └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # Makefile.in 2 | src/Makefile 3 | 4 | # Prerequisites 5 | *.d 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dSYM 21 | *.dll 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.out 31 | 32 | # other 33 | *.swp 34 | *.swo 35 | -------------------------------------------------------------------------------- /src/config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_fastdfs_module 2 | 3 | GCC_VERSION=$(gcc -dM -E - < /dev/null | grep -w __GNUC__ | awk '{print $NF;}') 4 | if [ -n "$GCC_VERSION" ] && [ $GCC_VERSION -ge 7 ]; then 5 | CFLAGS="$CFLAGS -Wformat-truncation=0 -Wformat-overflow=0" 6 | fi 7 | if test -n "${ngx_module_link}"; then 8 | ngx_module_type=HTTP 9 | ngx_module_name=$ngx_addon_name 10 | ngx_module_incs="/usr/local/include" 11 | ngx_module_libs="-lfastcommon -lserverframe -lfdfsclient" 12 | ngx_module_srcs="$ngx_addon_dir/ngx_http_fastdfs_module.c" 13 | ngx_module_deps= 14 | CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FILENAME='\"/etc/fdfs/mod_fastdfs.conf\"'" 15 | . auto/module 16 | else 17 | HTTP_MODULES="$HTTP_MODULES ngx_http_fastdfs_module" 18 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fastdfs_module.c" 19 | CORE_INCS="$CORE_INCS /usr/local/include" 20 | CORE_LIBS="$CORE_LIBS -lfastcommon -lserverframe -lfdfsclient" 21 | CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FILENAME='\"/etc/fdfs/mod_fastdfs.conf\"'" 22 | fi 23 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Copy right 2010 Happy Fish / YuQing 2 | 3 | This software may be copied only under the terms of the GNU General 4 | Public License V3, Please visit the FastDFS Home Page for more detail. 5 | Chinese language: http://www.fastken.com/ 6 | 7 | #step 1. first install the FastDFS storage server and client library, 8 | the FastDFS version should >= 6.13. download with git: 9 | github address: https://github.com/happyfish100/fastdfs 10 | gitee address: https://gitee.com/fastdfs100/fastdfs.git 11 | command lines as: 12 | 13 | git clone https://github.com/happyfish100/fastdfs 14 | cd fastdfs; git checkout V6.13.0 15 | ./make.sh clean && ./make.sh && ./make.sh install 16 | 17 | #step 2. download nginx server source code from http://nginx.org/ 18 | FastDFS nginx module test passed with nginx 1.16.1, 19 | my nginx installed in /usr/local/nginx 20 | 21 | #step 3. download FastDFS nginx module source code, 22 | github address: https://github.com/happyfish100/fastdfs-nginx-module 23 | gitee address: https://gitee.com/fastdfs100/fastdfs-nginx-module.git 24 | command lines as (YOUR_PATH is your base path eg. /home/yuqing ): 25 | 26 | cd $YOUR_PATH 27 | git clone https://github.com/happyfish100/fastdfs-nginx-module 28 | cd fastdfs-nginx-module; git checkout V1.25 29 | 30 | #step 4. enter the nginx source dir, compile and install fastdfs module, such as: 31 | 32 | cd nginx-1.16.1 33 | ./configure --add-module=$YOUR_PATH/fastdfs-nginx-module/src 34 | make; make install 35 | 36 | Notice: 37 | * replace $YOUR_PATH with your fastdfs-nginx-module base path, such as /home/yuqing 38 | * before compile, you can change FDFS_OUTPUT_CHUNK_SIZE and 39 | FDFS_MOD_CONF_FILENAME macro in the config file as: 40 | CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -DFDFS_OUTPUT_CHUNK_SIZE='256*1024' -DFDFS_MOD_CONF_FILENAME='\"/etc/fdfs/mod_fastdfs.conf\"'" 41 | 42 | #step 5. config the nginx config file such as nginx.conf, add the following lines: 43 | 44 | location /M00 { 45 | root /home/yuqing/fastdfs/data; 46 | ngx_fastdfs_module; 47 | } 48 | 49 | #step 6. make a symbol link ${fastdfs_base_path}/data/M00 to ${fastdfs_base_path}/data, 50 | command line such as: 51 | ln -s /home/yuqing/fastdfs/data /home/yuqing/fastdfs/data/M00 52 | 53 | #step 7. copy conf/http.conf and conf/mime.types in FastDFS source path to /etc/fdfs/ and modify http.conf, such as: 54 | cd /home/yuqing/fastdfs 55 | cp conf/http.conf conf/mime.types /etc/fdfs/ 56 | 57 | #step 8. copy mod_fastdfs.conf to /etc/fdfs/ and modify it 58 | 59 | #step 9. restart the nginx server, such as: 60 | /usr/local/nginx/sbin/nginx -s stop; /usr/local/nginx/sbin/nginx 61 | 62 | #step 10. view nginx log file, such as: 63 | tail -n 100 /usr/local/logs/error.log 64 | 65 | -------------------------------------------------------------------------------- /HISTORY: -------------------------------------------------------------------------------- 1 | 2 | Version 1.27 2025-11-25 3 | * anti-steal check bypass with specified HTTP header name and value 4 | 5 | Version 1.26 2025-11-12 6 | * you must upgrade your FastDFS to v6.15.1 or higher version 7 | 8 | Version 1.25 2025-08-31 9 | * performance opt.: replace sprintf and snprintf as necessary 10 | * call ngx_http_upstream_init directly 11 | * set u->resolved->sockaddr etc. 12 | * upstream proxy support IPv6 13 | * you must upgrade your FastDFS to v6.13 or higher version 14 | 15 | Version 1.24 2023-12-05 16 | * you must upgrade your FastDFS to v6.11 or higher version 17 | 18 | Version 1.23 2022-09-15 19 | * you must upgrade your FastDFS to v6.09 or higher version 20 | 21 | Version 1.22 2019-11-19 22 | * you must upgrade your FastDFS to v6.03 or higher version 23 | 24 | Version 1.21 2019-10-16 25 | * you must upgrade your FastDFS to v6.00 or higher version 26 | 27 | Version 1.20 2018-07-12 28 | * support multi regions for HTTP Range 29 | * you must upgrade your FastDFS to v5.12 or higher version 30 | 31 | Version 1.19 2017-04-14 32 | * change INT64_PRINTF_FORMAT to PRId64 33 | * ignore range->end >= file_size 34 | * fix HTTP status when file id is invalid 35 | 36 | Version 1.18 2016-02-24 37 | * bug fixed: do NOT use header_only field of request, 38 | use method field instead 39 | 40 | Version 1.17 2014-11-22 41 | * change include path of fastcommon and fastdfs 42 | you must upgrade your FastDFS server to v5.04 43 | or higher version 44 | 45 | Version 1.16 2014-05-04 46 | * mod_fastdfs.conf remove parameter: http.need_find_content_type 47 | * bug fixed: remove flv duplicate start variable define 48 | * flv support end parameter 49 | * bug fixed: in function proxy_create_request, use r->unparsed_uri 50 | when r->valid_unparsed_uri is true, otherwise use r->uri and r->args 51 | 52 | Version 1.15 2013-01-13 53 | * add storage server port config with multi groups 54 | * support flv, you must upgrade your FastDFS server to v4.06 55 | or higher version 56 | 57 | Version 1.14 2012-12-29 58 | * bug fixed: do not check store path index when not same group 59 | you must upgrade your FastDFS server to v4.05 or higher version 60 | * proxy handler deal headers more gracefully 61 | * set last modified time correctly 62 | * support multi groups 63 | 64 | Version 1.13 2012-11-14 65 | * support filename include storage server ID 66 | must upgrade your FastDFS server to v4.03 or higher version 67 | * call function fdfs_mod_init change from ngx_http_fastdfs_set to 68 | ngx_http_fastdfs_process_init 69 | 70 | Version 1.12 2012-10-20 71 | * log detail error info when stat file fail 72 | * mod_fastdfs.conf add parameter: load_fdfs_parameters_from_tracker, 73 | not load parameters from the FDFS tracker server by default 74 | 75 | Version 1.11 2012-08-27 76 | * add more debug info 77 | * use nginx error page when HTTP status is not ok 78 | 79 | Version 1.10 2012-01-03 80 | * use r->uri and r->args instead of r->unparsed_uri to support 81 | rewrite correctly 82 | 83 | Version 1.09 2011-11-26 84 | * bug fixed: correct HTTP status when nginx send file with range 85 | 86 | Version 1.08 2011-08-21 87 | * add store path count in startup log info 88 | * support HTTP range 89 | 90 | Version 1.07 2011-07-31 91 | * add more error log 92 | * bug fixed: miss sub dir data in data path 93 | 94 | Version 1.06 2011-06-11 95 | * bug fixed: cross group does not proxy or redirect when the file upload 96 | one day ago 97 | * support trunk file (FastDFS server version >= 3.00) 98 | 99 | Version 1.05 2011-03-16 100 | * support request header if_modified_since 101 | 102 | Version 1.04 2011-03-12 103 | * support url parameter "filename" to specify attachment filename 104 | * add Last-Modified header 105 | 106 | Version 1.03 2011-01-31 107 | * use FastDFS V2.08 client library 108 | 109 | Version 1.02 2010-12-20 110 | * fix signedness and signed assignment 111 | * change ngx_http_fastdfs_proxy_handler return type from ngx_int_t to int 112 | 113 | Version 1.01 2010-11-15 114 | * use nginx proxy deal approach 115 | 116 | Version 1.00 2010-11-08 117 | * first version 118 | 119 | -------------------------------------------------------------------------------- /src/mod_fastdfs.conf: -------------------------------------------------------------------------------- 1 | # connect timeout in seconds 2 | # default value is 30s 3 | connect_timeout=2 4 | 5 | # network recv and send timeout in seconds 6 | # default value is 30s 7 | network_timeout=30 8 | 9 | # the base path to store log files 10 | base_path=/tmp 11 | 12 | # if load FastDFS parameters from tracker server 13 | # since V1.12 14 | # default value is false 15 | load_fdfs_parameters_from_tracker=true 16 | 17 | # storage sync file max delay seconds 18 | # same as tracker.conf 19 | # valid only when load_fdfs_parameters_from_tracker is false 20 | # since V1.12 21 | # default value is 86400 seconds (one day) 22 | storage_sync_file_max_delay = 86400 23 | 24 | # if use storage ID instead of IP address 25 | # same as tracker.conf 26 | # valid only when load_fdfs_parameters_from_tracker is false 27 | # default value is false 28 | # since V1.13 29 | use_storage_id = false 30 | 31 | # specify storage ids filename, can use relative or absolute path 32 | # same as tracker.conf 33 | # valid only when load_fdfs_parameters_from_tracker is false 34 | # since V1.13 35 | storage_ids_filename = storage_ids.conf 36 | 37 | # FastDFS tracker_server can ocur more than once, and tracker_server format is 38 | # "host:port", host can be hostname or ip address 39 | # valid only when load_fdfs_parameters_from_tracker is true 40 | tracker_server=tracker:22122 41 | 42 | # the port of the local storage server 43 | # the default value is 23000 44 | storage_server_port=23000 45 | 46 | # the group name of the local storage server 47 | group_name=group1 48 | 49 | # if the url / uri including the group name 50 | # set to false when uri like /M00/00/00/xxx 51 | # set to true when uri like ${group_name}/M00/00/00/xxx, such as group1/M00/xxx 52 | # default value is false 53 | url_have_group_name = false 54 | 55 | # path(disk or mount point) count, default value is 1 56 | # must same as storage.conf 57 | store_path_count=1 58 | 59 | # store_path#, based 0, if store_path0 not exists, it's value is base_path 60 | # the paths must be exist 61 | # must same as storage.conf 62 | store_path0=/home/yuqing/fastdfs 63 | #store_path1=/home/yuqing/fastdfs1 64 | 65 | # standard log level as syslog, case insensitive, value list: 66 | ### emerg for emergency 67 | ### alert 68 | ### crit for critical 69 | ### error 70 | ### warn for warning 71 | ### notice 72 | ### info 73 | ### debug 74 | log_level=info 75 | 76 | # set the log filename, such as /usr/local/apache2/logs/mod_fastdfs.log 77 | # empty for output to stderr (apache and nginx error_log file) 78 | log_filename= 79 | 80 | # response mode when the file not exist in the local file system 81 | ## proxy: get the content from other storage server, then send to client 82 | ## redirect: redirect to the original storage server (HTTP Header is Location) 83 | response_mode=proxy 84 | 85 | # the NIC alias prefix, such as eth in Linux, you can see it by ifconfig -a 86 | # multi aliases split by comma. empty value means auto set by OS type 87 | # this paramter used to get all ip address of the local host 88 | # default values is empty 89 | if_alias_prefix= 90 | 91 | # use "#include" directive to include HTTP config file 92 | # NOTE: #include is an include directive, do NOT remove the # before include 93 | #include http.conf 94 | 95 | 96 | # if support flv 97 | # default value is false 98 | # since v1.15 99 | flv_support = true 100 | 101 | # flv file extension name 102 | # default value is flv 103 | # since v1.15 104 | flv_extension = flv 105 | 106 | 107 | # set the group count 108 | # set to none zero to support multi-group on this storage server 109 | # set to 0 for single group only 110 | # groups settings section as [group1], [group2], ..., [groupN] 111 | # default value is 0 112 | # since v1.14 113 | group_count = 0 114 | 115 | # group settings for group #1 116 | # since v1.14 117 | # when support multi-group on this storage server, uncomment following section 118 | #[group1] 119 | #group_name=group1 120 | #storage_server_port=23000 121 | #store_path_count=2 122 | #store_path0=/home/yuqing/fastdfs 123 | #store_path1=/home/yuqing/fastdfs1 124 | 125 | # group settings for group #2 126 | # since v1.14 127 | # when support multi-group, uncomment following section as neccessary 128 | #[group2] 129 | #group_name=group2 130 | #storage_server_port=23000 131 | #store_path_count=1 132 | #store_path0=/home/yuqing/fastdfs 133 | 134 | # custom request header to bypass anti-steal check. 135 | # example Nginx usage: proxy_set_header MY-HEADER 'MY-MARK' 136 | # disabled if commented out. 137 | #anti_steal_bypass_header_name=MY-HEADER 138 | #anti_steal_bypass_header_value=MY-MARK 139 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2008 Happy Fish / YuQing 3 | * 4 | * FastDFS may be copied only under the terms of the GNU General 5 | * Public License V3, which may be found in the FastDFS source kit. 6 | * Please visit the FastDFS Home Page http://www.fastken.com/ for more detail. 7 | **/ 8 | 9 | #ifndef FDFS_MOD_COMMON_H 10 | #define FDFS_MOD_COMMON_H 11 | 12 | #include 13 | #include "fastdfs/tracker_types.h" 14 | 15 | #ifndef HTTP_OK 16 | #define HTTP_OK 200 17 | #endif 18 | 19 | #ifndef HTTP_NOCONTENT 20 | #define HTTP_NOCONTENT 204 21 | #endif 22 | 23 | #ifndef HTTP_PARTIAL_CONTENT 24 | #define HTTP_PARTIAL_CONTENT 206 25 | #endif 26 | 27 | #ifndef HTTP_MOVEPERM 28 | #define HTTP_MOVEPERM 301 29 | #endif 30 | 31 | #ifndef HTTP_MOVETEMP 32 | #define HTTP_MOVETEMP 302 33 | #endif 34 | 35 | #ifndef HTTP_NOTMODIFIED 36 | #define HTTP_NOTMODIFIED 304 37 | #endif 38 | 39 | #ifndef HTTP_BADREQUEST 40 | #define HTTP_BADREQUEST 400 41 | #endif 42 | 43 | #ifndef HTTP_NOTFOUND 44 | #define HTTP_NOTFOUND 404 45 | #endif 46 | 47 | #ifndef HTTP_RANGE_NOT_SATISFIABLE 48 | #define HTTP_RANGE_NOT_SATISFIABLE 416 49 | #endif 50 | 51 | #ifndef HTTP_INTERNAL_SERVER_ERROR 52 | #define HTTP_INTERNAL_SERVER_ERROR 500 53 | #endif 54 | 55 | #ifndef HTTP_SERVUNAVAIL 56 | #define HTTP_SERVUNAVAIL 503 57 | #endif 58 | 59 | #ifndef FDFS_STORAGE_STORE_PATH_PREFIX_CHAR 60 | #define FDFS_STORAGE_STORE_PATH_PREFIX_CHAR 'M' 61 | #endif 62 | 63 | #define FDFS_MAX_HTTP_RANGES 5 64 | #define FDFS_MAX_HTTP_BOUNDARY 20 65 | 66 | #ifdef __cplusplus 67 | extern "C" { 68 | #endif 69 | 70 | struct fdfs_http_response; 71 | 72 | typedef const char *(*FDFSGetRequestHeader)(void *arg, const string_t *name, string_t *value); 73 | typedef void (*FDFSOutputHeaders)(void *arg, struct fdfs_http_response *pResponse); 74 | typedef int (*FDFSSendReplyChunk)(void *arg, const bool last_buff, \ 75 | const char *buff, const int size); 76 | typedef int (*FDFSSendFile)(void *arg, const char *filename, \ 77 | const int filename_len, const int64_t file_offset, \ 78 | const int64_t download_bytes); 79 | 80 | typedef int (*FDFSProxyHandler)(void *arg, const char *dest_ip_addr); 81 | 82 | struct fdfs_http_resp_content_range { 83 | int length; 84 | char content[64]; 85 | }; 86 | 87 | struct fdfs_http_response { 88 | int status; //HTTP status 89 | time_t last_modified; //last modified time of the file 90 | int redirect_url_len; 91 | int range_len; //for redirect 92 | int boundary_len; 93 | short content_range_count; 94 | int64_t content_length; 95 | char *content_type; 96 | char *attachment_filename; 97 | char redirect_url[256]; 98 | char content_disposition[256]; 99 | char content_type_buff[128]; 100 | char range[256]; //for redirect 101 | char range_content_type[64]; //for multi range content 102 | struct fdfs_http_resp_content_range content_ranges[FDFS_MAX_HTTP_RANGES]; 103 | char last_modified_buff[32]; 104 | char boundary[FDFS_MAX_HTTP_BOUNDARY]; 105 | bool header_outputed; //if header output 106 | }; 107 | 108 | struct fdfs_http_range { 109 | int64_t start; 110 | int64_t end; 111 | }; 112 | 113 | struct fdfs_http_context { 114 | int server_port; 115 | short range_count; 116 | bool header_only; 117 | bool if_range; 118 | struct fdfs_http_range ranges[FDFS_MAX_HTTP_RANGES]; 119 | char if_modified_since[32]; 120 | char *url; 121 | void *arg; //for callback 122 | FDFSOutputHeaders output_headers; 123 | FDFSGetRequestHeader get_request_header; 124 | FDFSSendFile send_file; //nginx send file 125 | FDFSSendReplyChunk send_reply_chunk; 126 | FDFSProxyHandler proxy_handler; //nginx proxy handler 127 | }; 128 | 129 | struct fdfs_download_callback_args { 130 | struct fdfs_http_context *pContext; 131 | struct fdfs_http_response *pResponse; 132 | int64_t sent_bytes; //sent bytes 133 | int range_index; 134 | }; 135 | 136 | /** 137 | * init function 138 | * params: 139 | * return: 0 success, !=0 fail, return the error code 140 | */ 141 | int fdfs_mod_init(); 142 | 143 | /** 144 | * http request handler 145 | * params: 146 | * pContext the context 147 | * return: http status code, HTTP_OK success, != HTTP_OK fail 148 | */ 149 | int fdfs_http_request_handler(struct fdfs_http_context *pContext); 150 | 151 | /** 152 | * format http datetime 153 | * params: 154 | * t the time 155 | * buff the string buffer 156 | * buff_size the buffer size 157 | * return: 0 success, !=0 fail, return the error code 158 | */ 159 | //int fdfs_format_http_datetime(time_t t, char *buff, const int buff_size); 160 | 161 | /** 162 | * parse range parameter 163 | * params: 164 | * value the range value 165 | * pContext the context 166 | * return: 0 success, !=0 fail, return the error code 167 | */ 168 | int fdfs_parse_ranges(const char *value, struct fdfs_http_context *pContext); 169 | 170 | #ifdef __cplusplus 171 | } 172 | #endif 173 | 174 | #endif 175 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/ngx_http_fastdfs_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "common.c" 7 | 8 | typedef struct { 9 | ngx_http_upstream_conf_t upstream; 10 | ngx_uint_t headers_hash_max_size; 11 | ngx_uint_t headers_hash_bucket_size; 12 | } ngx_http_fastdfs_loc_conf_t; 13 | 14 | static char *ngx_http_fastdfs_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 15 | 16 | static ngx_int_t ngx_http_fastdfs_process_init(ngx_cycle_t *cycle); 17 | static void ngx_http_fastdfs_process_exit(ngx_cycle_t *cycle); 18 | 19 | static int ngx_http_fastdfs_proxy_handler(void *arg, const char *dest_ip_addr); 20 | 21 | static ngx_int_t ngx_http_fastdfs_proxy_process_status_line(ngx_http_request_t *r); 22 | static ngx_int_t ngx_http_fastdfs_proxy_process_header(ngx_http_request_t *r); 23 | 24 | static void *ngx_http_fastdfs_create_loc_conf(ngx_conf_t *cf); 25 | static char *ngx_http_fastdfs_merge_loc_conf(ngx_conf_t *cf, 26 | void *parent, void *child); 27 | 28 | typedef struct { 29 | ngx_http_status_t status; 30 | char dest_ip_addr[IP_ADDRESS_SIZE]; 31 | } ngx_http_fastdfs_proxy_ctx_t; 32 | 33 | static char ngx_http_fastdfs_proxy_version[] = " HTTP/1.0"CRLF; 34 | 35 | static ngx_str_t ngx_http_proxy_hide_headers[] = { 36 | ngx_string("Date"), 37 | ngx_string("Server"), 38 | ngx_string("X-Pad"), 39 | ngx_string("X-Accel-Expires"), 40 | ngx_string("X-Accel-Redirect"), 41 | ngx_string("X-Accel-Limit-Rate"), 42 | ngx_string("X-Accel-Buffering"), 43 | ngx_string("X-Accel-Charset"), 44 | ngx_null_string 45 | }; 46 | 47 | /* Commands */ 48 | static ngx_command_t ngx_http_fastdfs_commands[] = { 49 | { ngx_string("ngx_fastdfs_module"), 50 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, 51 | ngx_http_fastdfs_set, 52 | NGX_HTTP_LOC_CONF_OFFSET, 53 | 0, 54 | NULL }, 55 | 56 | ngx_null_command 57 | }; 58 | 59 | static ngx_http_module_t ngx_http_fastdfs_module_ctx = { 60 | NULL, /* preconfiguration */ 61 | NULL, /* postconfiguration */ 62 | 63 | NULL, /* create main configuration */ 64 | NULL, /* init main configuration */ 65 | 66 | NULL, /* create server configuration */ 67 | NULL, /* merge server configuration */ 68 | 69 | ngx_http_fastdfs_create_loc_conf, /* create location configration */ 70 | ngx_http_fastdfs_merge_loc_conf /* merge location configration */ 71 | }; 72 | 73 | /* hook */ 74 | ngx_module_t ngx_http_fastdfs_module = { 75 | NGX_MODULE_V1, 76 | &ngx_http_fastdfs_module_ctx, /* module context */ 77 | ngx_http_fastdfs_commands, /* module directives */ 78 | NGX_HTTP_MODULE, /* module type */ 79 | NULL, /* init master */ 80 | NULL, /* init module */ 81 | ngx_http_fastdfs_process_init, /* init process */ 82 | NULL, /* init thread */ 83 | NULL, /* exit thread */ 84 | ngx_http_fastdfs_process_exit, /* exit process */ 85 | NULL, /* exit master */ 86 | NGX_MODULE_V1_PADDING 87 | }; 88 | 89 | static ngx_int_t fdfs_set_header(ngx_http_request_t *r, \ 90 | const char *key, const char *low_key, const int key_len, \ 91 | char *value, const int value_len) 92 | { 93 | ngx_table_elt_t *cc; 94 | 95 | cc = ngx_list_push(&r->headers_out.headers); 96 | if (cc == NULL) 97 | { 98 | return NGX_ERROR; 99 | } 100 | 101 | cc->hash = 1; 102 | cc->key.len = key_len; 103 | cc->key.data = (u_char *)key; 104 | cc->lowcase_key = (u_char *)low_key; 105 | cc->value.len = value_len; 106 | cc->value.data = (u_char *)value; 107 | 108 | return NGX_OK; 109 | } 110 | 111 | static ngx_int_t fdfs_set_content_disposition(ngx_http_request_t *r, 112 | struct fdfs_http_response *pResponse) 113 | { 114 | #define ATTACHMENT_PREFIX_STR "attachment; filename=\"" 115 | #define ATTACHMENT_PREFIX_LEN (sizeof(ATTACHMENT_PREFIX_STR) - 1) 116 | 117 | int value_len; 118 | int file_len; 119 | char *p; 120 | 121 | file_len = strlen(pResponse->attachment_filename); 122 | if (file_len + ATTACHMENT_PREFIX_LEN + 2 > 123 | sizeof(pResponse->content_disposition)) 124 | { 125 | file_len = sizeof(pResponse->content_disposition) - 126 | (ATTACHMENT_PREFIX_LEN + 2); 127 | } 128 | 129 | p = pResponse->content_disposition; 130 | memcpy(p, ATTACHMENT_PREFIX_STR, ATTACHMENT_PREFIX_LEN); 131 | p += ATTACHMENT_PREFIX_LEN; 132 | memcpy(p, pResponse->attachment_filename, file_len); 133 | p += file_len; 134 | *p++ = '"'; 135 | *p = '\0'; 136 | value_len = p - pResponse->content_disposition; 137 | return fdfs_set_header(r, "Content-Disposition", 138 | "content-disposition", 139 | sizeof("Content-Disposition") - 1, 140 | pResponse->content_disposition, value_len); 141 | } 142 | 143 | static ngx_int_t fdfs_set_range(ngx_http_request_t *r, \ 144 | struct fdfs_http_response *pResponse) 145 | { 146 | return fdfs_set_header(r, "Range", "range", \ 147 | sizeof("Range") - 1, pResponse->range, pResponse->range_len); 148 | } 149 | 150 | static ngx_int_t fdfs_set_content_range(ngx_http_request_t *r, \ 151 | struct fdfs_http_response *pResponse) 152 | { 153 | return fdfs_set_header(r, "Content-Range", "content-range", \ 154 | sizeof("Content-Range") - 1, pResponse->content_ranges[0].content, \ 155 | pResponse->content_ranges[0].length); 156 | } 157 | 158 | static ngx_int_t fdfs_set_accept_ranges(ngx_http_request_t *r) 159 | { 160 | return fdfs_set_header(r, "Accept-Ranges", "accept-ranges", \ 161 | sizeof("Accept-Ranges") - 1, "bytes", sizeof("bytes") - 1); 162 | } 163 | 164 | static ngx_int_t fdfs_set_location(ngx_http_request_t *r, \ 165 | struct fdfs_http_response *pResponse) 166 | { 167 | ngx_table_elt_t *cc; 168 | 169 | cc = r->headers_out.location; 170 | if (cc == NULL) 171 | { 172 | cc = ngx_list_push(&r->headers_out.headers); 173 | if (cc == NULL) 174 | { 175 | return NGX_ERROR; 176 | } 177 | 178 | cc->hash = 1; 179 | cc->key.len = sizeof("Location") - 1; 180 | cc->key.data = (u_char *)"Location"; 181 | cc->lowcase_key = (u_char *)"location"; 182 | } 183 | 184 | cc->value.len = pResponse->redirect_url_len; 185 | cc->value.data = (u_char *)pResponse->redirect_url; 186 | 187 | return NGX_OK; 188 | } 189 | 190 | static void fdfs_output_headers(void *arg, struct fdfs_http_response *pResponse) 191 | { 192 | ngx_http_request_t *r; 193 | ngx_int_t rc; 194 | 195 | if (pResponse->header_outputed) 196 | { 197 | return; 198 | } 199 | 200 | r = (ngx_http_request_t *)arg; 201 | 202 | if (pResponse->status != HTTP_OK \ 203 | && pResponse->status != HTTP_PARTIAL_CONTENT) 204 | { 205 | if (pResponse->status == HTTP_MOVETEMP) 206 | { 207 | if (pResponse->range_len > 0) 208 | { 209 | fdfs_set_range(r, pResponse); 210 | } 211 | fdfs_set_location(r, pResponse); 212 | } 213 | else 214 | { 215 | return; //does not send http header for other status 216 | } 217 | } 218 | else 219 | { 220 | if (pResponse->content_type != NULL) 221 | { 222 | r->headers_out.content_type.len = strlen(pResponse->content_type); 223 | r->headers_out.content_type.data = (u_char *)pResponse->content_type; 224 | } 225 | 226 | r->headers_out.content_length_n = pResponse->content_length; 227 | if (pResponse->attachment_filename != NULL) 228 | { 229 | fdfs_set_content_disposition(r, pResponse); 230 | } 231 | 232 | r->headers_out.last_modified_time = pResponse->last_modified; 233 | fdfs_set_accept_ranges(r); 234 | if (pResponse->content_range_count == 1) 235 | { 236 | fdfs_set_content_range(r, pResponse); 237 | } 238 | } 239 | 240 | ngx_http_set_content_type(r); 241 | 242 | r->headers_out.status = pResponse->status; 243 | pResponse->header_outputed = true; 244 | if (pResponse->content_length <= 0) 245 | { 246 | r->header_only = 1; 247 | } 248 | rc = ngx_http_send_header(r); 249 | if (rc == NGX_ERROR || rc > NGX_OK) 250 | { 251 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 252 | "ngx_http_send_header fail, return code=%d", rc); 253 | return; 254 | } 255 | } 256 | 257 | static const char *fdfs_get_request_header(void *arg, const string_t *name, string_t *value) 258 | { 259 | ngx_http_request_t *r; 260 | ngx_uint_t i; 261 | ngx_list_part_t *part; 262 | ngx_table_elt_t *header; 263 | 264 | r = (ngx_http_request_t *)arg; 265 | part = &r->headers_in.headers.part; 266 | header = part->elts; 267 | for (i = 0; /* void */; i++) 268 | { 269 | if (i >= part->nelts) 270 | { 271 | if (part->next == NULL) 272 | { 273 | break; 274 | } 275 | 276 | part = part->next; 277 | header = part->elts; 278 | i = 0; 279 | } 280 | if ((int)header[i].key.len == name->len && ngx_strncasecmp(header[i]. 281 | key.data, (u_char *)name->str, header[i].key.len) == 0) 282 | { 283 | FC_SET_STRING_EX(*value, (char *)header[i].value.data, 284 | header[i].value.len); 285 | return value->str; 286 | } 287 | } 288 | 289 | FC_SET_STRING_NULL(*value); 290 | return NULL; 291 | } 292 | 293 | static int fdfs_send_reply_chunk(void *arg, const bool last_buf, 294 | const char *buff, const int size) 295 | { 296 | ngx_http_request_t *r; 297 | ngx_buf_t *b; 298 | ngx_chain_t out; 299 | ngx_int_t rc; 300 | u_char *new_buff; 301 | 302 | r = (ngx_http_request_t *)arg; 303 | 304 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); 305 | if (b == NULL) 306 | { 307 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 308 | "ngx_pcalloc fail"); 309 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 310 | } 311 | 312 | new_buff = ngx_palloc(r->pool, sizeof(u_char) * size); 313 | if (new_buff == NULL) 314 | { 315 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 316 | "ngx_palloc fail"); 317 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 318 | } 319 | 320 | out.buf = b; 321 | out.next = NULL; 322 | 323 | memcpy(new_buff, buff, size); 324 | 325 | b->pos = (u_char *)new_buff; 326 | b->last = (u_char *)new_buff + size; 327 | b->memory = 1; 328 | b->last_in_chain = last_buf; 329 | b->last_buf = last_buf; 330 | 331 | rc = ngx_http_output_filter(r, &out); 332 | if (rc == NGX_OK || rc == NGX_AGAIN) 333 | { 334 | return 0; 335 | } 336 | else 337 | { 338 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 339 | "file: "__FILE__", line: %d, ngx_http_output_filter fail, " 340 | "return code: %d, buff size: %d, last_buf: %d, " 341 | "header sent: %d, sent bytes: %d", __LINE__, rc, 342 | size, last_buf, r->header_sent, r->connection->sent); 343 | return rc; 344 | } 345 | } 346 | 347 | static int fdfs_send_file(void *arg, const char *filename, 348 | const int filename_len, const int64_t file_offset, 349 | const int64_t download_bytes) 350 | { 351 | ngx_http_request_t *r; 352 | ngx_http_core_loc_conf_t *ccf; 353 | ngx_buf_t *b; 354 | ngx_str_t ngx_filename; 355 | ngx_open_file_info_t of; 356 | ngx_chain_t out; 357 | ngx_uint_t level; 358 | ngx_int_t rc; 359 | 360 | r = (ngx_http_request_t *)arg; 361 | 362 | ccf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 363 | 364 | ngx_filename.data = (u_char *)filename; 365 | ngx_filename.len = filename_len; 366 | 367 | ngx_memzero(&of, sizeof(ngx_open_file_info_t)); 368 | 369 | #if defined(nginx_version) && (nginx_version >= 8018) 370 | of.read_ahead = ccf->read_ahead; 371 | #endif 372 | of.directio = ccf->directio; 373 | of.valid = ccf->open_file_cache_valid; 374 | of.min_uses = ccf->open_file_cache_min_uses; 375 | of.errors = ccf->open_file_cache_errors; 376 | of.events = ccf->open_file_cache_events; 377 | if (ngx_open_cached_file(ccf->open_file_cache, &ngx_filename, 378 | &of, r->pool) != NGX_OK) 379 | { 380 | switch (of.err) 381 | { 382 | case 0: 383 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 384 | case NGX_ENOENT: 385 | case NGX_ENOTDIR: 386 | case NGX_ENAMETOOLONG: 387 | level = NGX_LOG_ERR; 388 | rc = NGX_HTTP_NOT_FOUND; 389 | break; 390 | case NGX_EACCES: 391 | level = NGX_LOG_ERR; 392 | rc = NGX_HTTP_FORBIDDEN; 393 | break; 394 | default: 395 | level = NGX_LOG_CRIT; 396 | rc = NGX_HTTP_INTERNAL_SERVER_ERROR; 397 | break; 398 | } 399 | 400 | if (rc != NGX_HTTP_NOT_FOUND || ccf->log_not_found) 401 | { 402 | ngx_log_error(level, r->connection->log, of.err, \ 403 | "%s \"%s\" failed", of.failed, filename); 404 | } 405 | 406 | return rc; 407 | } 408 | 409 | if (!of.is_file) 410 | { 411 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, \ 412 | "\"%s\" is not a regular file", filename); 413 | return NGX_HTTP_NOT_FOUND; 414 | } 415 | 416 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); 417 | if (b == NULL) 418 | { 419 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 420 | "ngx_pcalloc fail"); 421 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 422 | } 423 | 424 | b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t)); 425 | if (b->file == NULL) 426 | { 427 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 428 | } 429 | 430 | out.buf = b; 431 | out.next = NULL; 432 | 433 | b->file_pos = file_offset; 434 | b->file_last = file_offset + download_bytes; 435 | b->in_file = download_bytes > 0 ? 1 : 0; 436 | b->file->fd = of.fd; 437 | b->file->name.data = (u_char *)filename; 438 | b->file->name.len = filename_len; 439 | b->file->log = r->connection->log; 440 | b->file->directio = of.is_directio; 441 | 442 | b->last_in_chain = 1; 443 | b->last_buf = 1; 444 | 445 | rc = ngx_http_output_filter(r, &out); 446 | if (rc == NGX_OK || rc == NGX_AGAIN) 447 | { 448 | return NGX_HTTP_OK; 449 | } 450 | else 451 | { 452 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 453 | "file: "__FILE__", line: %d, ngx_http_output_filter fail, " 454 | "return code: %d, file offset: %"PRId64", download bytes: " 455 | "%"PRId64", header sent: %d, sent bytes: %d", __LINE__, 456 | rc, file_offset, download_bytes, r->header_sent, 457 | r->connection->sent); 458 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 459 | } 460 | } 461 | 462 | static ngx_int_t ngx_http_fastdfs_proxy_create_request(ngx_http_request_t *r) 463 | { 464 | #define FDFS_REDIRECT_PARAM "redirect=1" 465 | 466 | size_t len; 467 | ngx_buf_t *b; 468 | ngx_uint_t i; 469 | ngx_chain_t *cl; 470 | ngx_list_part_t *part; 471 | ngx_table_elt_t *header; 472 | ngx_http_upstream_t *u; 473 | char *p; 474 | char url[4096]; 475 | char *the_url; 476 | size_t url_len; 477 | bool have_query; 478 | 479 | u = r->upstream; 480 | if (r->valid_unparsed_uri) 481 | { 482 | the_url = (char *)r->unparsed_uri.data; 483 | url_len = r->unparsed_uri.len; 484 | have_query = memchr(the_url, '?', url_len) != NULL; 485 | } 486 | else 487 | { 488 | if (r->uri.len + r->args.len + 1 >= sizeof(url)) 489 | { 490 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 491 | "url too long, exceeds %d bytes!", (int)sizeof(url)); 492 | return NGX_ERROR; 493 | } 494 | 495 | p = url; 496 | memcpy(p, r->uri.data, r->uri.len); 497 | p += r->uri.len; 498 | if (r->args.len > 0) 499 | { 500 | *p++ = '?'; 501 | memcpy(p, r->args.data, r->args.len); 502 | p += r->args.len; 503 | have_query = true; 504 | } 505 | else 506 | { 507 | have_query = false; 508 | } 509 | 510 | the_url = url; 511 | url_len = p - url; 512 | } 513 | 514 | len = r->method_name.len + 1 + url_len + 1 + 515 | sizeof(FDFS_REDIRECT_PARAM) - 1 + 1 + 516 | sizeof(ngx_http_fastdfs_proxy_version) - 1 + sizeof(CRLF) - 1; 517 | 518 | part = &r->headers_in.headers.part; 519 | header = part->elts; 520 | 521 | for (i = 0; /* void */; i++) 522 | { 523 | if (i >= part->nelts) 524 | { 525 | if (part->next == NULL) 526 | { 527 | break; 528 | } 529 | 530 | part = part->next; 531 | header = part->elts; 532 | i = 0; 533 | } 534 | 535 | len += header[i].key.len + 2 + header[i].value.len + 536 | sizeof(CRLF) - 1; 537 | } 538 | 539 | b = ngx_create_temp_buf(r->pool, len); 540 | if (b == NULL) 541 | { 542 | return NGX_ERROR; 543 | } 544 | 545 | cl = ngx_alloc_chain_link(r->pool); 546 | if (cl == NULL) 547 | { 548 | return NGX_ERROR; 549 | } 550 | 551 | cl->buf = b; 552 | 553 | /* the request line */ 554 | b->last = ngx_copy(b->last, r->method_name.data, r->method_name.len); 555 | *b->last++ = ' '; 556 | 557 | u->uri.data = b->last; 558 | b->last = ngx_cpymem(b->last, the_url, url_len); 559 | 560 | if (have_query) 561 | { 562 | *b->last++ = '&'; 563 | } 564 | else 565 | { 566 | *b->last++ = '?'; 567 | } 568 | b->last = ngx_cpymem(b->last, FDFS_REDIRECT_PARAM, 569 | sizeof(FDFS_REDIRECT_PARAM) - 1); 570 | 571 | u->uri.len = b->last - u->uri.data; 572 | 573 | *b->last++ = ' '; 574 | b->last = ngx_cpymem(b->last, ngx_http_fastdfs_proxy_version, 575 | sizeof(ngx_http_fastdfs_proxy_version) - 1); 576 | 577 | part = &r->headers_in.headers.part; 578 | header = part->elts; 579 | for (i = 0; /* void */; i++) 580 | { 581 | if (i >= part->nelts) 582 | { 583 | if (part->next == NULL) 584 | { 585 | break; 586 | } 587 | 588 | part = part->next; 589 | header = part->elts; 590 | i = 0; 591 | } 592 | 593 | b->last = ngx_copy(b->last, header[i].key.data, 594 | header[i].key.len); 595 | *b->last++ = ':'; *b->last++ = ' '; 596 | b->last = ngx_copy(b->last, header[i].value.data, 597 | header[i].value.len); 598 | *b->last++ = CR; *b->last++ = LF; 599 | } 600 | 601 | /* add "\r\n" at the header end */ 602 | *b->last++ = CR; *b->last++ = LF; 603 | 604 | /* 605 | fprintf(stderr, "http proxy header(%d, %d):\n\"%*s\"\n", 606 | len, b->last - b->pos, (b->last - b->pos), b->pos); 607 | */ 608 | 609 | u->request_bufs = cl; 610 | cl->next = NULL; 611 | 612 | return NGX_OK; 613 | } 614 | 615 | static ngx_int_t ngx_http_fastdfs_proxy_reinit_request(ngx_http_request_t *r) 616 | { 617 | ngx_http_fastdfs_proxy_ctx_t *ctx; 618 | 619 | ctx = ngx_http_get_module_ctx(r, ngx_http_fastdfs_module); 620 | 621 | if (ctx == NULL) { 622 | return NGX_OK; 623 | } 624 | 625 | ctx->status.code = 0; 626 | ctx->status.count = 0; 627 | ctx->status.start = NULL; 628 | ctx->status.end = NULL; 629 | 630 | r->upstream->process_header = ngx_http_fastdfs_proxy_process_status_line; 631 | 632 | return NGX_OK; 633 | } 634 | 635 | static void ngx_http_fastdfs_proxy_abort_request(ngx_http_request_t *r) 636 | { 637 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 638 | "abort http proxy request"); 639 | 640 | return; 641 | } 642 | 643 | 644 | static void ngx_http_fastdfs_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc) 645 | { 646 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 647 | "finalize http proxy request"); 648 | 649 | return; 650 | } 651 | 652 | static ngx_int_t ngx_http_fastdfs_proxy_process_status_line(ngx_http_request_t *r) 653 | { 654 | size_t len; 655 | ngx_int_t rc; 656 | ngx_http_upstream_t *u; 657 | ngx_http_fastdfs_proxy_ctx_t *ctx; 658 | 659 | ctx = ngx_http_get_module_ctx(r, ngx_http_fastdfs_module); 660 | 661 | if (ctx == NULL) { 662 | return NGX_ERROR; 663 | } 664 | 665 | u = r->upstream; 666 | 667 | rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status); 668 | 669 | if (rc == NGX_AGAIN) { 670 | return rc; 671 | } 672 | 673 | if (rc == NGX_ERROR) { 674 | 675 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 676 | "upstream sent no valid HTTP/1.0 header"); 677 | 678 | #if 0 679 | if (u->accel) { 680 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 681 | } 682 | #endif 683 | 684 | r->http_version = NGX_HTTP_VERSION_9; 685 | u->state->status = NGX_HTTP_OK; 686 | 687 | return NGX_OK; 688 | } 689 | 690 | if (u->state) { 691 | u->state->status = ctx->status.code; 692 | } 693 | 694 | u->headers_in.status_n = ctx->status.code; 695 | 696 | len = ctx->status.end - ctx->status.start; 697 | u->headers_in.status_line.len = len; 698 | 699 | u->headers_in.status_line.data = ngx_pnalloc(r->pool, len); 700 | if (u->headers_in.status_line.data == NULL) { 701 | return NGX_ERROR; 702 | } 703 | 704 | ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len); 705 | 706 | u->process_header = ngx_http_fastdfs_proxy_process_header; 707 | 708 | return ngx_http_fastdfs_proxy_process_header(r); 709 | } 710 | 711 | static ngx_int_t ngx_http_fastdfs_proxy_process_header(ngx_http_request_t *r) 712 | { 713 | ngx_int_t rc; 714 | ngx_table_elt_t *h; 715 | ngx_http_upstream_header_t *hh; 716 | ngx_http_upstream_main_conf_t *umcf; 717 | 718 | umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module); 719 | 720 | for ( ;; ) { 721 | rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1); 722 | if (rc == NGX_OK) { 723 | 724 | /* a header line has been parsed successfully */ 725 | 726 | h = ngx_list_push(&r->upstream->headers_in.headers); 727 | if (h == NULL) { 728 | return NGX_ERROR; 729 | } 730 | 731 | h->hash = r->header_hash; 732 | 733 | h->key.len = r->header_name_end - r->header_name_start; 734 | h->value.len = r->header_end - r->header_start; 735 | 736 | h->key.data = ngx_pnalloc(r->pool, 737 | h->key.len + 1 + h->value.len + 1 + h->key.len); 738 | if (h->key.data == NULL) { 739 | return NGX_ERROR; 740 | } 741 | 742 | h->value.data = h->key.data + h->key.len + 1; 743 | h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1; 744 | 745 | ngx_memcpy(h->key.data, r->header_name_start, h->key.len); 746 | h->key.data[h->key.len] = '\0'; 747 | ngx_memcpy(h->value.data, r->header_start, h->value.len); 748 | h->value.data[h->value.len] = '\0'; 749 | 750 | if (h->key.len == r->lowcase_index) { 751 | ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); 752 | } else { 753 | ngx_strlow(h->lowcase_key, h->key.data, h->key.len); 754 | } 755 | 756 | hh = ngx_hash_find(&umcf->headers_in_hash, h->hash, 757 | h->lowcase_key, h->key.len); 758 | if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { 759 | return NGX_ERROR; 760 | } 761 | 762 | /* 763 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 764 | "http proxy header: \"%V: %V\"", 765 | &h->key, &h->value); 766 | */ 767 | 768 | continue; 769 | } 770 | 771 | if (rc == NGX_HTTP_PARSE_HEADER_DONE) { 772 | 773 | /* a whole header has been parsed successfully */ 774 | /* 775 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 776 | "http proxy header done"); 777 | */ 778 | /* 779 | * if no "Server" and "Date" in header line, 780 | * then add the special empty headers 781 | */ 782 | 783 | if (r->upstream->headers_in.server == NULL) { 784 | h = ngx_list_push(&r->upstream->headers_in.headers); 785 | if (h == NULL) { 786 | return NGX_ERROR; 787 | } 788 | 789 | h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash( 790 | ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r'); 791 | 792 | ngx_str_set(&h->key, "Server"); 793 | ngx_str_null(&h->value); 794 | h->lowcase_key = (u_char *) "server"; 795 | } 796 | 797 | if (r->upstream->headers_in.date == NULL) { 798 | h = ngx_list_push(&r->upstream->headers_in.headers); 799 | if (h == NULL) { 800 | return NGX_ERROR; 801 | } 802 | 803 | h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e'); 804 | 805 | ngx_str_set(&h->key, "Date"); 806 | ngx_str_null(&h->value); 807 | h->lowcase_key = (u_char *) "date"; 808 | } 809 | 810 | /* clear content length if response is chunked */ 811 | /* 812 | if (r->upstream->headers_in.chunked) { 813 | r->upstream->headers_in.content_length_n = -1; 814 | } 815 | */ 816 | 817 | return NGX_OK; 818 | } 819 | 820 | if (rc == NGX_AGAIN) { 821 | return NGX_AGAIN; 822 | } 823 | 824 | /* there was error while a header line parsing */ 825 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 826 | "upstream sent invalid header"); 827 | 828 | return NGX_HTTP_UPSTREAM_INVALID_HEADER; 829 | } 830 | } 831 | 832 | static int ngx_http_fastdfs_proxy_handler(void *arg, 833 | const char *dest_ip_addr) 834 | { 835 | ngx_http_request_t *r; 836 | ngx_http_upstream_t *u; 837 | ngx_http_fastdfs_proxy_ctx_t *ctx; 838 | ngx_http_fastdfs_loc_conf_t *plcf; 839 | int network_port; 840 | 841 | r = (ngx_http_request_t *)arg; 842 | 843 | if (ngx_http_upstream_create(r) != NGX_OK) { 844 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 845 | } 846 | 847 | ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_fastdfs_proxy_ctx_t)); 848 | if (ctx == NULL) { 849 | return NGX_ERROR; 850 | } 851 | 852 | ngx_http_set_ctx(r, ctx, ngx_http_fastdfs_module); 853 | 854 | plcf = ngx_http_get_module_loc_conf(r, ngx_http_fastdfs_module); 855 | 856 | u = r->upstream; 857 | 858 | #if (NGX_HTTP_SSL) 859 | u->ssl = (plcf->upstream.ssl != NULL); 860 | #endif 861 | 862 | u->conf = &plcf->upstream; 863 | 864 | u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t)); 865 | if (u->resolved == NULL) 866 | { 867 | return NGX_ERROR; 868 | } 869 | 870 | ngx_str_set(&u->schema, "http://"); 871 | strcpy(ctx->dest_ip_addr, dest_ip_addr); 872 | 873 | if (r->connection->local_socklen == sizeof(struct sockaddr_in)) 874 | { 875 | network_port = ((struct sockaddr_in *)r-> 876 | connection->local_sockaddr)->sin_port; 877 | } 878 | else 879 | { 880 | network_port = ((struct sockaddr_in6 *)r-> 881 | connection->local_sockaddr)->sin6_port; 882 | } 883 | 884 | if (is_ipv6_addr(dest_ip_addr)) 885 | { 886 | struct sockaddr_in6 *addr6 = ngx_pcalloc(r->pool, 887 | sizeof(struct sockaddr_in6)); 888 | if (addr6 == NULL) 889 | { 890 | return NGX_ERROR; 891 | } 892 | addr6->sin6_family = AF_INET6; 893 | addr6->sin6_port = network_port; 894 | if (inet_pton(AF_INET6, dest_ip_addr, &addr6->sin6_addr) == 0) 895 | { 896 | logError("file: "__FILE__", line: %d, " 897 | "invalid IPv6 ip address: %s", __LINE__, 898 | dest_ip_addr); 899 | return NGX_ERROR; 900 | } 901 | u->resolved->sockaddr = (struct sockaddr *)addr6; 902 | u->resolved->socklen = sizeof(struct sockaddr_in6); 903 | u->resolved->naddrs = 1; 904 | } 905 | else 906 | { 907 | struct sockaddr_in *addr4 = ngx_pcalloc(r->pool, 908 | sizeof(struct sockaddr_in)); 909 | if (addr4 == NULL) 910 | { 911 | return NGX_ERROR; 912 | } 913 | addr4->sin_family = AF_INET; 914 | addr4->sin_port = network_port; 915 | addr4->sin_addr.s_addr = inet_addr(dest_ip_addr); 916 | u->resolved->sockaddr = (struct sockaddr *)addr4; 917 | u->resolved->socklen = sizeof(struct sockaddr_in); 918 | u->resolved->naddrs = 1; 919 | } 920 | 921 | u->resolved->host.data = (u_char *)ctx->dest_ip_addr; 922 | u->resolved->host.len = strlen(ctx->dest_ip_addr); 923 | u->resolved->port = (in_port_t)ntohs(network_port); 924 | 925 | u->output.tag = (ngx_buf_tag_t) &ngx_http_fastdfs_module; 926 | 927 | u->create_request = ngx_http_fastdfs_proxy_create_request; 928 | u->reinit_request = ngx_http_fastdfs_proxy_reinit_request; 929 | u->process_header = ngx_http_fastdfs_proxy_process_status_line; 930 | u->abort_request = ngx_http_fastdfs_proxy_abort_request; 931 | u->finalize_request = ngx_http_fastdfs_proxy_finalize_request; 932 | 933 | r->main->count++; 934 | ngx_http_upstream_init(r); 935 | return NGX_DONE; 936 | } 937 | 938 | static ngx_int_t ngx_http_fastdfs_handler(ngx_http_request_t *r) 939 | { 940 | struct fdfs_http_context context; 941 | ngx_int_t rc; 942 | char url[4096]; 943 | char *p; 944 | 945 | if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) { 946 | return NGX_HTTP_NOT_ALLOWED; 947 | } 948 | 949 | rc = ngx_http_discard_request_body(r); 950 | if (rc != NGX_OK && rc != NGX_AGAIN) 951 | { 952 | return rc; 953 | } 954 | 955 | if (r->uri.len + r->args.len + 1 >= sizeof(url)) 956 | { 957 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 958 | "url too long, exceeds %d bytes!", (int)sizeof(url)); 959 | return HTTP_BADREQUEST; 960 | } 961 | 962 | p = url; 963 | memcpy(p, r->uri.data, r->uri.len); 964 | p += r->uri.len; 965 | if (r->args.len > 0) 966 | { 967 | *p++ = '?'; 968 | memcpy(p, r->args.data, r->args.len); 969 | p += r->args.len; 970 | } 971 | *p = '\0'; 972 | 973 | memset(&context, 0, sizeof(context)); 974 | context.arg = r; 975 | context.header_only = (r->method & NGX_HTTP_HEAD) ? 1 : 0; 976 | context.url = url; 977 | context.output_headers = fdfs_output_headers; 978 | context.get_request_header = fdfs_get_request_header; 979 | context.send_file = fdfs_send_file; 980 | context.send_reply_chunk = fdfs_send_reply_chunk; 981 | context.proxy_handler = ngx_http_fastdfs_proxy_handler; 982 | context.server_port = ntohs(((struct sockaddr_in *)r->connection-> 983 | local_sockaddr)->sin_port); 984 | 985 | if (r->headers_in.if_modified_since != NULL) 986 | { 987 | if (r->headers_in.if_modified_since->value.len < \ 988 | sizeof(context.if_modified_since)) 989 | { 990 | memcpy(context.if_modified_since, \ 991 | r->headers_in.if_modified_since->value.data, \ 992 | r->headers_in.if_modified_since->value.len); 993 | } 994 | 995 | /* 996 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \ 997 | "if_modified_since: %s", context.if_modified_since); 998 | */ 999 | } 1000 | 1001 | if (r->headers_in.range != NULL) 1002 | { 1003 | char buff[64]; 1004 | if (r->headers_in.range->value.len >= sizeof(buff)) 1005 | { 1006 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \ 1007 | "bad request, range length: %d exceeds buff " \ 1008 | "size: %d, range: %*s", \ 1009 | r->headers_in.range->value.len, \ 1010 | (int)sizeof(buff), \ 1011 | r->headers_in.range->value.len, \ 1012 | r->headers_in.range->value.data); 1013 | return NGX_HTTP_BAD_REQUEST; 1014 | } 1015 | 1016 | memcpy(buff, r->headers_in.range->value.data, \ 1017 | r->headers_in.range->value.len); 1018 | *(buff + r->headers_in.range->value.len) = '\0'; 1019 | //ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "buff=%s", buff); 1020 | if (fdfs_parse_ranges(buff, &context) != 0) 1021 | { 1022 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \ 1023 | "bad request, invalid range: %s", buff); 1024 | return NGX_HTTP_RANGE_NOT_SATISFIABLE; 1025 | } 1026 | context.if_range = true; 1027 | 1028 | /* 1029 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \ 1030 | "if_range=%d, start=%d, end=%d", context.if_range, \ 1031 | (int)context.range.start, (int)context.range.end); 1032 | */ 1033 | } 1034 | 1035 | /* 1036 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \ 1037 | "args=%*s, uri=%*s", r->args.len, r->args.data, \ 1038 | r->uri.len, r->uri.data); 1039 | */ 1040 | 1041 | return fdfs_http_request_handler(&context); 1042 | } 1043 | 1044 | static char *ngx_http_fastdfs_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1045 | { 1046 | ngx_http_core_loc_conf_t *clcf = ngx_http_conf_get_module_loc_conf(cf, \ 1047 | ngx_http_core_module); 1048 | 1049 | fprintf(stderr, "ngx_http_fastdfs_set pid=%d\n", getpid()); 1050 | 1051 | /* register hanlder */ 1052 | clcf->handler = ngx_http_fastdfs_handler; 1053 | 1054 | return NGX_CONF_OK; 1055 | } 1056 | 1057 | static ngx_int_t ngx_http_fastdfs_process_init(ngx_cycle_t *cycle) 1058 | { 1059 | int result; 1060 | 1061 | fprintf(stderr, "ngx_http_fastdfs_process_init pid=%d\n", getpid()); 1062 | // do some init here 1063 | if ((result=fdfs_mod_init()) != 0) 1064 | { 1065 | return NGX_ERROR; 1066 | } 1067 | 1068 | return NGX_OK; 1069 | } 1070 | 1071 | static void ngx_http_fastdfs_process_exit(ngx_cycle_t *cycle) 1072 | { 1073 | fprintf(stderr, "ngx_http_fastdfs_process_exit pid=%d\n", getpid()); 1074 | return; 1075 | } 1076 | 1077 | static void *ngx_http_fastdfs_create_loc_conf(ngx_conf_t *cf) 1078 | { 1079 | ngx_http_fastdfs_loc_conf_t *conf; 1080 | 1081 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastdfs_loc_conf_t)); 1082 | if (conf == NULL) { 1083 | return NULL; 1084 | } 1085 | 1086 | conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC; 1087 | conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC; 1088 | conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC; 1089 | 1090 | conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE; 1091 | 1092 | conf->upstream.hide_headers = NGX_CONF_UNSET_PTR; 1093 | conf->upstream.pass_headers = NGX_CONF_UNSET_PTR; 1094 | 1095 | /* the hardcoded values */ 1096 | conf->upstream.cyclic_temp_file = 0; 1097 | conf->upstream.buffering = 0; 1098 | conf->upstream.ignore_client_abort = 0; 1099 | conf->upstream.send_lowat = 0; 1100 | conf->upstream.bufs.num = 0; 1101 | conf->upstream.busy_buffers_size = 0; 1102 | conf->upstream.max_temp_file_size = 0; 1103 | conf->upstream.temp_file_write_size = 0; 1104 | conf->upstream.intercept_errors = 1; 1105 | conf->upstream.intercept_404 = 1; 1106 | conf->upstream.pass_request_headers = 0; 1107 | conf->upstream.pass_request_body = 0; 1108 | 1109 | conf->headers_hash_max_size = NGX_CONF_UNSET_UINT; 1110 | conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT; 1111 | 1112 | return conf; 1113 | } 1114 | 1115 | static char * ngx_http_fastdfs_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 1116 | { 1117 | ngx_hash_init_t hash; 1118 | ngx_http_fastdfs_loc_conf_t *prev = parent; 1119 | ngx_http_fastdfs_loc_conf_t *conf = child; 1120 | 1121 | ngx_conf_merge_msec_value(conf->upstream.connect_timeout, 1122 | prev->upstream.connect_timeout, 60000); 1123 | 1124 | ngx_conf_merge_msec_value(conf->upstream.send_timeout, 1125 | prev->upstream.send_timeout, 60000); 1126 | 1127 | ngx_conf_merge_msec_value(conf->upstream.read_timeout, 1128 | prev->upstream.read_timeout, 60000); 1129 | 1130 | ngx_conf_merge_size_value(conf->upstream.buffer_size, 1131 | prev->upstream.buffer_size, 1132 | (size_t) ngx_pagesize); 1133 | 1134 | ngx_conf_merge_bitmask_value(conf->upstream.next_upstream, 1135 | prev->upstream.next_upstream, 1136 | (NGX_CONF_BITMASK_SET 1137 | |NGX_HTTP_UPSTREAM_FT_ERROR 1138 | |NGX_HTTP_UPSTREAM_FT_TIMEOUT)); 1139 | 1140 | if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) { 1141 | conf->upstream.next_upstream = NGX_CONF_BITMASK_SET 1142 | |NGX_HTTP_UPSTREAM_FT_OFF; 1143 | } 1144 | 1145 | if (conf->upstream.upstream == NULL) { 1146 | conf->upstream.upstream = prev->upstream.upstream; 1147 | } 1148 | 1149 | ngx_conf_merge_uint_value(conf->headers_hash_max_size, 1150 | prev->headers_hash_max_size, 512); 1151 | 1152 | ngx_conf_merge_uint_value(conf->headers_hash_bucket_size, 1153 | prev->headers_hash_bucket_size, 64); 1154 | conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size, 1155 | ngx_cacheline_size); 1156 | 1157 | hash.max_size = conf->headers_hash_max_size; 1158 | hash.bucket_size = conf->headers_hash_bucket_size; 1159 | hash.name = "proxy_headers_hash"; 1160 | 1161 | if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream, 1162 | &prev->upstream, ngx_http_proxy_hide_headers, &hash) 1163 | != NGX_OK) 1164 | { 1165 | return NGX_CONF_ERROR; 1166 | } 1167 | 1168 | return NGX_CONF_OK; 1169 | } 1170 | 1171 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2008 Happy Fish / YuQing 3 | * 4 | * FastDFS may be copied only under the terms of the GNU General 5 | * Public License V3, which may be found in the FastDFS source kit. 6 | * Please visit the FastDFS Home Page http://www.fastken.com/ for more detail. 7 | **/ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "fastcommon/logger.h" 22 | #include "fastcommon/shared_func.h" 23 | #include "fastcommon/sockopt.h" 24 | #include "fastcommon/http_func.h" 25 | #include "fastcommon/local_ip_func.h" 26 | #include "fastdfs/fdfs_define.h" 27 | #include "fastdfs/fdfs_global.h" 28 | #include "fastdfs/fdfs_http_shared.h" 29 | #include "fastdfs/fdfs_client.h" 30 | #include "fastdfs/fdfs_shared_func.h" 31 | #include "fastdfs/trunk_shared.h" 32 | #include "common.h" 33 | 34 | #define FDFS_MOD_REPONSE_MODE_PROXY 'P' 35 | #define FDFS_MOD_REPONSE_MODE_REDIRECT 'R' 36 | 37 | #define FDFS_CONTENT_TYPE_TAG_STR "Content-type: " 38 | #define FDFS_CONTENT_TYPE_TAG_LEN (sizeof(FDFS_CONTENT_TYPE_TAG_STR) - 1) 39 | #define FDFS_CONTENT_RANGE_TAG_STR "Content-range: " 40 | #define FDFS_CONTENT_RANGE_TAG_LEN (sizeof(FDFS_CONTENT_RANGE_TAG_STR) - 1) 41 | 42 | static char flv_header[] = "FLV\x1\x1\0\0\0\x9\0\0\0\x9"; 43 | 44 | #define FDFS_RANGE_LENGTH(range) ((range.end - range.start) + 1) 45 | 46 | typedef struct tagGroupStorePaths { 47 | char group_name[FDFS_GROUP_NAME_MAX_LEN + 1]; 48 | int group_name_len; 49 | int storage_server_port; 50 | FDFSStorePaths store_paths; 51 | } GroupStorePaths; 52 | 53 | static int storage_server_port = FDFS_STORAGE_SERVER_DEF_PORT; 54 | static int my_group_name_len = 0; 55 | static int group_count = 0; //for multi groups 56 | static bool url_have_group_name = false; 57 | static bool use_storage_id = false; 58 | static bool flv_support = false; //if support flv 59 | static char flv_extension[FDFS_FILE_EXT_NAME_MAX_LEN + 1] = {0}; //flv extension name 60 | static int flv_ext_len = 0; //flv extension length 61 | static char my_group_name[FDFS_GROUP_NAME_MAX_LEN + 1] = {0}; 62 | static char response_mode = FDFS_MOD_REPONSE_MODE_PROXY; 63 | static GroupStorePaths *group_store_paths = NULL; //for multi groups 64 | static FDFSHTTPParams g_http_params; 65 | static int storage_sync_file_max_delay = 24 * 3600; 66 | static char bypass_header_name_buff[256]; 67 | static char bypass_header_value_buff[256]; 68 | static string_t bypass_header_name; 69 | static string_t bypass_header_value; 70 | 71 | static int fdfs_get_params_from_tracker(); 72 | static int fdfs_format_http_datetime(time_t t, char *buff, const int buff_size); 73 | 74 | static int fdfs_strtoll(const char *s, int64_t *value) 75 | { 76 | char *end = NULL; 77 | *value = strtoll(s, &end, 10); 78 | if (end != NULL && *end != '\0') 79 | { 80 | return EINVAL; 81 | } 82 | 83 | return 0; 84 | } 85 | 86 | static int fdfs_load_groups_store_paths(IniContext *pItemContext) 87 | { 88 | #define FDFS_GROUP_PREFIX_STR "group" 89 | #define FDFS_GROUP_PREFIX_LEN (sizeof(FDFS_GROUP_PREFIX_STR) - 1) 90 | 91 | char section_name[64]; 92 | char *pGroupName; 93 | int bytes; 94 | int result; 95 | int i; 96 | 97 | bytes = sizeof(GroupStorePaths) * group_count; 98 | group_store_paths = (GroupStorePaths *)malloc(bytes); 99 | if (group_store_paths == NULL) 100 | { 101 | logError("file: "__FILE__", line: %d, " \ 102 | "malloc %d bytes fail, " \ 103 | "errno: %d, error info: %s", \ 104 | __LINE__, bytes, errno, STRERROR(errno)); 105 | return errno != 0 ? errno : ENOMEM; 106 | } 107 | 108 | memcpy(section_name, FDFS_GROUP_PREFIX_STR, FDFS_GROUP_PREFIX_LEN); 109 | for (i=0; i 0) 187 | { 188 | if (!url_have_group_name) 189 | { 190 | logError("file: "__FILE__", line: %d, " \ 191 | "config file: %s, you must set " \ 192 | "url_have_group_name to true to " \ 193 | "support multi-group!", \ 194 | __LINE__, FDFS_MOD_CONF_FILENAME); 195 | result = ENOENT; 196 | break; 197 | } 198 | 199 | if ((result=fdfs_load_groups_store_paths(&iniContext)) != 0) 200 | { 201 | break; 202 | } 203 | } 204 | else 205 | { 206 | char *pGroupName; 207 | 208 | pGroupName = iniGetStrValue(NULL, "group_name", &iniContext); 209 | if (pGroupName == NULL) 210 | { 211 | logError("file: "__FILE__", line: %d, " \ 212 | "config file: %s, you must set parameter: " \ 213 | "group_name!", __LINE__, FDFS_MOD_CONF_FILENAME); 214 | result = ENOENT; 215 | break; 216 | } 217 | 218 | my_group_name_len = fc_safe_strcpy(my_group_name, pGroupName); 219 | if (my_group_name_len == 0) 220 | { 221 | logError("file: "__FILE__", line: %d, " \ 222 | "config file: %s, parameter: group_name " \ 223 | "can't be empty!", __LINE__, \ 224 | FDFS_MOD_CONF_FILENAME); 225 | result = EINVAL; 226 | break; 227 | } 228 | 229 | if ((result=storage_load_paths_from_conf_file(&iniContext, 230 | FDFS_MOD_CONF_FILENAME)) != 0) 231 | { 232 | break; 233 | } 234 | } 235 | 236 | SF_G_CONNECT_TIMEOUT = iniGetIntValue(NULL, "connect_timeout", \ 237 | &iniContext, DEFAULT_CONNECT_TIMEOUT); 238 | if (SF_G_CONNECT_TIMEOUT <= 0) 239 | { 240 | SF_G_CONNECT_TIMEOUT = DEFAULT_CONNECT_TIMEOUT; 241 | } 242 | 243 | SF_G_NETWORK_TIMEOUT = iniGetIntValue(NULL, "network_timeout", \ 244 | &iniContext, DEFAULT_NETWORK_TIMEOUT); 245 | if (SF_G_NETWORK_TIMEOUT <= 0) 246 | { 247 | SF_G_NETWORK_TIMEOUT = DEFAULT_NETWORK_TIMEOUT; 248 | } 249 | 250 | load_log_level(&iniContext); 251 | 252 | pLogFilename = iniGetStrValue(NULL, "log_filename", &iniContext); 253 | if (pLogFilename != NULL && *pLogFilename != '\0') 254 | { 255 | if ((result=log_set_filename(pLogFilename)) != 0) 256 | { 257 | break; 258 | } 259 | } 260 | 261 | storage_server_port = iniGetIntValue(NULL, "storage_server_port", \ 262 | &iniContext, FDFS_STORAGE_SERVER_DEF_PORT); 263 | 264 | if ((result=fdfs_http_params_load(&iniContext, FDFS_MOD_CONF_FILENAME, \ 265 | &g_http_params)) != 0) 266 | { 267 | break; 268 | } 269 | 270 | pReponseMode = iniGetStrValue(NULL, "response_mode", &iniContext); 271 | if (pReponseMode != NULL) 272 | { 273 | if (strcmp(pReponseMode, "redirect") == 0) 274 | { 275 | response_mode = FDFS_MOD_REPONSE_MODE_REDIRECT; 276 | } 277 | } 278 | 279 | pIfAliasPrefix = iniGetStrValue (NULL, "if_alias_prefix", &iniContext); 280 | if (pIfAliasPrefix == NULL) 281 | { 282 | *g_if_alias_prefix = '\0'; 283 | } 284 | else 285 | { 286 | fc_safe_strcpy(g_if_alias_prefix, pIfAliasPrefix); 287 | } 288 | 289 | load_fdfs_parameters_from_tracker = iniGetBoolValue(NULL, \ 290 | "load_fdfs_parameters_from_tracker", \ 291 | &iniContext, false); 292 | if (load_fdfs_parameters_from_tracker) 293 | { 294 | result = fdfs_load_tracker_group_ex(&g_tracker_group, \ 295 | FDFS_MOD_CONF_FILENAME, &iniContext); 296 | } 297 | else 298 | { 299 | storage_sync_file_max_delay = iniGetIntValue(NULL, \ 300 | "storage_sync_file_max_delay", \ 301 | &iniContext, 24 * 3600); 302 | use_storage_id = iniGetBoolValue(NULL, "use_storage_id", \ 303 | &iniContext, false); 304 | if (use_storage_id) 305 | { 306 | result = fdfs_load_storage_ids_from_file( \ 307 | FDFS_MOD_CONF_FILENAME, &iniContext); 308 | } 309 | } 310 | 311 | } while (false); 312 | 313 | flv_support = iniGetBoolValue(NULL, "flv_support", \ 314 | &iniContext, false); 315 | if (flv_support) 316 | { 317 | char *flvExtension; 318 | flvExtension = iniGetStrValue (NULL, "flv_extension", \ 319 | &iniContext); 320 | if (flvExtension == NULL) 321 | { 322 | strcpy(flv_extension, "flv"); 323 | flv_ext_len = 3; 324 | } 325 | else 326 | { 327 | flv_ext_len = fc_safe_strcpy(flv_extension, flvExtension); 328 | } 329 | } 330 | 331 | FC_SET_STRING_EMPTY(bypass_header_name, bypass_header_name_buff); 332 | FC_SET_STRING_EMPTY(bypass_header_value, bypass_header_value_buff); 333 | 334 | bypassHeaderName = iniGetStrValue(NULL, 335 | "anti_steal_bypass_header_name", &iniContext); 336 | if (bypassHeaderName != NULL) { 337 | bypass_header_name.len = fc_safe_strcpy(bypass_header_name_buff, 338 | bypassHeaderName); 339 | 340 | bypassHeaderValue = iniGetStrValue(NULL, 341 | "anti_steal_bypass_header_value", &iniContext); 342 | if (bypassHeaderValue != NULL) { 343 | bypass_header_value.len = fc_safe_strcpy(bypass_header_value_buff, 344 | bypassHeaderValue); 345 | } 346 | } 347 | 348 | iniFreeContext(&iniContext); 349 | if (result != 0) 350 | { 351 | return result; 352 | } 353 | 354 | load_local_host_ip_addrs(); 355 | if (load_fdfs_parameters_from_tracker) 356 | { 357 | fdfs_get_params_from_tracker(); 358 | } 359 | 360 | if (group_count > 0) 361 | { 362 | len = sprintf(buff, "group_count=%d, ", group_count); 363 | } 364 | else 365 | { 366 | len = sprintf(buff, "group_name=%s, storage_server_port=%d, " \ 367 | "path_count=%d, ", my_group_name, \ 368 | storage_server_port, g_fdfs_store_paths.count); 369 | for (i=0; i 0) 413 | { 414 | int k; 415 | for (k=0; kstatus = http_status; \ 441 | pContext->output_headers(pContext->arg, pResponse); \ 442 | } while (0) 443 | 444 | static int fdfs_send_boundary(struct fdfs_http_context *pContext, 445 | struct fdfs_http_response *pResponse, const bool bLast) 446 | { 447 | int result; 448 | 449 | if ((result=pContext->send_reply_chunk(pContext->arg, 450 | false, "\r\n--", 4)) != 0) 451 | { 452 | return result; 453 | } 454 | 455 | if ((result=pContext->send_reply_chunk(pContext->arg, 456 | false, pResponse->boundary, 457 | pResponse->boundary_len)) != 0) 458 | { 459 | return result; 460 | } 461 | 462 | if (bLast) 463 | { 464 | result = pContext->send_reply_chunk(pContext->arg, true, "--\r\n", 4); 465 | } 466 | else 467 | { 468 | result = pContext->send_reply_chunk(pContext->arg, false, "\r\n", 2); 469 | } 470 | return result; 471 | } 472 | 473 | static int fdfs_send_range_subheader(struct fdfs_http_context *pContext, 474 | struct fdfs_http_response *pResponse, const int index) 475 | { 476 | char buff[256]; 477 | char *p; 478 | int content_type_len; 479 | 480 | content_type_len = strlen(pResponse->range_content_type); 481 | p = buff; 482 | memcpy(p, FDFS_CONTENT_TYPE_TAG_STR, FDFS_CONTENT_TYPE_TAG_LEN); 483 | p += FDFS_CONTENT_TYPE_TAG_LEN; 484 | memcpy(p, pResponse->range_content_type, content_type_len); 485 | p += content_type_len; 486 | *p++ = '\r'; 487 | *p++ = '\n'; 488 | memcpy(p, FDFS_CONTENT_RANGE_TAG_STR, FDFS_CONTENT_RANGE_TAG_LEN); 489 | p += FDFS_CONTENT_RANGE_TAG_LEN; 490 | memcpy(p, pResponse->content_ranges[index].content, 491 | pResponse->content_ranges[index].length); 492 | p += pResponse->content_ranges[index].length; 493 | *p++ = '\r'; 494 | *p++ = '\n'; 495 | *p++ = '\r'; 496 | *p++ = '\n'; 497 | *p = '\0'; 498 | return pContext->send_reply_chunk(pContext->arg, false, buff, p - buff); 499 | } 500 | 501 | static int fdfs_download_callback(void *arg, const int64_t file_size, \ 502 | const char *data, const int current_size) 503 | { 504 | struct fdfs_download_callback_args *pCallbackArgs; 505 | int result; 506 | bool bLast; 507 | 508 | pCallbackArgs = (struct fdfs_download_callback_args *)arg; 509 | 510 | if (!pCallbackArgs->pResponse->header_outputed) 511 | { 512 | if (!(pCallbackArgs->pContext->if_range && 513 | pCallbackArgs->pContext->range_count > 1)) 514 | { 515 | pCallbackArgs->pResponse->content_length = file_size; 516 | } 517 | OUTPUT_HEADERS(pCallbackArgs->pContext, 518 | pCallbackArgs->pResponse, HTTP_OK); 519 | } 520 | 521 | if (pCallbackArgs->pContext->if_range && pCallbackArgs-> 522 | pContext->range_count > 1) 523 | { 524 | bLast = false; 525 | if (pCallbackArgs->sent_bytes == 0) 526 | { 527 | if ((result=fdfs_send_boundary(pCallbackArgs->pContext, 528 | pCallbackArgs->pResponse, false)) != 0) 529 | { 530 | return result; 531 | } 532 | if ((result=fdfs_send_range_subheader(pCallbackArgs->pContext, 533 | pCallbackArgs->pResponse, 534 | pCallbackArgs->range_index)) != 0) 535 | { 536 | return result; 537 | } 538 | } 539 | } 540 | else 541 | { 542 | bLast = true; 543 | } 544 | 545 | pCallbackArgs->sent_bytes += current_size; 546 | return pCallbackArgs->pContext->send_reply_chunk( 547 | pCallbackArgs->pContext->arg, 548 | (pCallbackArgs->sent_bytes == file_size && bLast) ? 1 : 0, 549 | data, current_size); 550 | } 551 | 552 | static void fdfs_do_format_range(const struct fdfs_http_range *range, 553 | struct fdfs_http_response *pResponse) 554 | { 555 | if (range->start < 0) 556 | { 557 | pResponse->range_len += fc_itoa(range->start, 558 | pResponse->range + pResponse->range_len); 559 | } 560 | else if (range->end == 0) 561 | { 562 | pResponse->range_len += fc_itoa(range->start, 563 | pResponse->range + pResponse->range_len); 564 | *(pResponse->range + pResponse->range_len++) = '-'; 565 | } 566 | else 567 | { 568 | pResponse->range_len += fc_itoa(range->start, 569 | pResponse->range + pResponse->range_len); 570 | *(pResponse->range + pResponse->range_len++) = '-'; 571 | pResponse->range_len += fc_itoa(range->end, 572 | pResponse->range + pResponse->range_len); 573 | } 574 | } 575 | 576 | static void fdfs_format_range(struct fdfs_http_context *pContext, 577 | struct fdfs_http_response *pResponse) 578 | { 579 | #define RANGE_BYTES_TAG_STR "bytes=" 580 | #define RANGE_BYTES_TAG_LEN (sizeof(RANGE_BYTES_TAG_STR) - 1) 581 | 582 | int i; 583 | 584 | memcpy(pResponse->range, RANGE_BYTES_TAG_STR, RANGE_BYTES_TAG_LEN); 585 | pResponse->range_len = RANGE_BYTES_TAG_LEN; 586 | for (i=0; irange_count; i++) 587 | { 588 | if (i > 0) 589 | { 590 | *(pResponse->range + pResponse->range_len) = ','; 591 | pResponse->range_len++; 592 | } 593 | fdfs_do_format_range(pContext->ranges + i, pResponse); 594 | } 595 | *(pResponse->range + pResponse->range_len) = '\0'; 596 | } 597 | 598 | static void fdfs_do_format_content_range(const struct fdfs_http_range *range, 599 | const int64_t file_size, struct fdfs_http_resp_content_range *content_range) 600 | { 601 | #define RANGE_BYTES_MARK_STR "bytes " 602 | #define RANGE_BYTES_MARK_LEN (sizeof(RANGE_BYTES_MARK_STR) - 1) 603 | char *p; 604 | 605 | p = content_range->content; 606 | memcpy(p, RANGE_BYTES_MARK_STR, RANGE_BYTES_MARK_LEN); 607 | p += RANGE_BYTES_MARK_LEN; 608 | p += fc_itoa(range->start, p); 609 | *p++ = '-'; 610 | p += fc_itoa(range->end, p); 611 | *p++ = '/'; 612 | p += fc_itoa(file_size, p); 613 | *p = '\0'; 614 | 615 | content_range->length = p - content_range->content; 616 | } 617 | 618 | static void fdfs_format_content_range(struct fdfs_http_context *pContext, 619 | const int64_t file_size, struct fdfs_http_response *pResponse) 620 | { 621 | int i; 622 | pResponse->content_range_count = pContext->range_count; 623 | for (i=0; irange_count; i++) 624 | { 625 | fdfs_do_format_content_range(pContext->ranges + i, 626 | file_size, pResponse->content_ranges + i); 627 | } 628 | } 629 | 630 | static int64_t fdfs_calc_download_bytes(struct fdfs_http_context *pContext) 631 | { 632 | int64_t download_bytes; 633 | int i; 634 | 635 | download_bytes = 0; 636 | for (i=0; irange_count; i++) 637 | { 638 | download_bytes += FDFS_RANGE_LENGTH(pContext->ranges[i]); 639 | } 640 | return download_bytes; 641 | } 642 | 643 | static int fdfs_calc_content_length(struct fdfs_http_context *pContext, 644 | const int64_t download_bytes, const int flv_header_len, 645 | const char *ext_name, const int ext_len, 646 | struct fdfs_http_response *pResponse) 647 | { 648 | #define MULTIPART_BYTERANGES_STR "multipart/byteranges; boundary=" 649 | #define MULTIPART_BYTERANGES_LEN (sizeof(MULTIPART_BYTERANGES_STR) - 1) 650 | 651 | int result; 652 | int i; 653 | int content_type_part_len; 654 | int boundary_part_len; 655 | 656 | pResponse->content_length = download_bytes + flv_header_len; 657 | if (pContext->if_range && pContext->range_count > 1) 658 | { 659 | pResponse->boundary_len = fc_ltostr(get_current_time_us(), 660 | pResponse->boundary); 661 | memcpy(pResponse->content_type_buff, MULTIPART_BYTERANGES_STR, 662 | MULTIPART_BYTERANGES_LEN); 663 | memcpy(pResponse->content_type_buff + MULTIPART_BYTERANGES_LEN, 664 | pResponse->boundary, pResponse->boundary_len); 665 | *(pResponse->content_type_buff + MULTIPART_BYTERANGES_LEN + 666 | pResponse->boundary_len) = '\0'; 667 | pResponse->content_type = pResponse->content_type_buff; 668 | 669 | if ((result=fdfs_http_get_content_type_by_extname(&g_http_params, 670 | ext_name, ext_len, pResponse->range_content_type, 671 | sizeof(pResponse->range_content_type))) != 0) 672 | { 673 | return result; 674 | } 675 | 676 | content_type_part_len = FDFS_CONTENT_TYPE_TAG_LEN + 677 | strlen(pResponse->range_content_type) + 2; 678 | boundary_part_len = 4 + pResponse->boundary_len + 2; 679 | 680 | pResponse->content_length += (pContext->range_count + 1) * boundary_part_len; 681 | pResponse->content_length += pContext->range_count * content_type_part_len; 682 | for (i=0; irange_count; i++) 683 | { 684 | pResponse->content_length += FDFS_CONTENT_RANGE_TAG_LEN + 685 | pResponse->content_ranges[i].length + 4; 686 | } 687 | 688 | pResponse->content_length += 2; //last -- 689 | } 690 | 691 | return 0; 692 | } 693 | 694 | static int fdfs_do_check_and_format_range(struct fdfs_http_range *range, 695 | const int64_t file_size) 696 | { 697 | if (range->start < 0) 698 | { 699 | int64_t start; 700 | start = range->start + file_size; 701 | if (start < 0) 702 | { 703 | logWarning("file: "__FILE__", line: %d, " \ 704 | "invalid range value: %"PRId64", set to 0", \ 705 | __LINE__, range->start); 706 | start = 0; 707 | } 708 | range->start = start; 709 | } 710 | else if (range->start >= file_size) 711 | { 712 | logError("file: "__FILE__", line: %d, " \ 713 | "invalid range start value: %"PRId64 \ 714 | ", exceeds file size: %"PRId64, \ 715 | __LINE__, range->start, file_size); 716 | return EINVAL; 717 | } 718 | 719 | if (range->end == 0) 720 | { 721 | range->end = file_size - 1; 722 | } 723 | else if (range->end >= file_size) 724 | { 725 | logWarning("file: "__FILE__", line: %d, " \ 726 | "invalid range end value: %"PRId64 \ 727 | ", exceeds file size: %"PRId64, \ 728 | __LINE__, range->end, file_size); 729 | range->end = file_size - 1; 730 | } 731 | 732 | if (range->start > range->end) 733 | { 734 | logError("file: "__FILE__", line: %d, " \ 735 | "invalid range value, start: %"PRId64 \ 736 | ", exceeds end: %"PRId64, \ 737 | __LINE__, range->start, range->end); 738 | return EINVAL; 739 | } 740 | 741 | return 0; 742 | } 743 | 744 | static int fdfs_check_and_format_range(struct fdfs_http_context *pContext, 745 | const int64_t file_size) 746 | { 747 | int result; 748 | int i; 749 | 750 | result = 0; 751 | for (i=0; irange_count; i++) 752 | { 753 | if ((result=fdfs_do_check_and_format_range(pContext->ranges + i, file_size)) != 0) 754 | { 755 | return result; 756 | } 757 | } 758 | 759 | return 0; 760 | } 761 | 762 | #define FDFS_SET_LAST_MODIFIED(response, pContext, mtime) \ 763 | do { \ 764 | response.last_modified = mtime; \ 765 | fdfs_format_http_datetime(response.last_modified, \ 766 | response.last_modified_buff, \ 767 | sizeof(response.last_modified_buff)); \ 768 | if (*pContext->if_modified_since != '\0') \ 769 | { \ 770 | if (strcmp(response.last_modified_buff, \ 771 | pContext->if_modified_since) == 0) \ 772 | { \ 773 | OUTPUT_HEADERS(pContext, (&response), HTTP_NOTMODIFIED);\ 774 | return HTTP_NOTMODIFIED; \ 775 | } \ 776 | } \ 777 | \ 778 | /*\ 779 | logInfo("last_modified: %s, if_modified_since: %s, strcmp=%d", \ 780 | response.last_modified_buff, \ 781 | pContext->if_modified_since, \ 782 | strcmp(response.last_modified_buff, \ 783 | pContext->if_modified_since)); \ 784 | */ \ 785 | } while (0) 786 | 787 | static int fdfs_send_file_buffer(struct fdfs_http_context *pContext, 788 | const char *full_filename, int fd, 789 | const int64_t download_bytes, const bool bLast) 790 | { 791 | char file_trunk_buff[FDFS_OUTPUT_CHUNK_SIZE]; 792 | off_t remain_bytes; 793 | int read_bytes; 794 | int result; 795 | 796 | remain_bytes = download_bytes; 797 | while (remain_bytes > 0) 798 | { 799 | read_bytes = remain_bytes <= FDFS_OUTPUT_CHUNK_SIZE ? \ 800 | remain_bytes : FDFS_OUTPUT_CHUNK_SIZE; 801 | if (read(fd, file_trunk_buff, read_bytes) != read_bytes) 802 | { 803 | result = errno != 0 ? errno : EIO; 804 | logError("file: "__FILE__", line: %d, " \ 805 | "read from file %s fail, " \ 806 | "errno: %d, error info: %s", __LINE__, \ 807 | full_filename, result, STRERROR(result)); 808 | return result; 809 | } 810 | 811 | remain_bytes -= read_bytes; 812 | if ((result=pContext->send_reply_chunk(pContext->arg, 813 | (remain_bytes == 0 && bLast) ? 1: 0, file_trunk_buff, 814 | read_bytes)) != 0) 815 | { 816 | return result; 817 | } 818 | } 819 | 820 | return 0; 821 | } 822 | 823 | int fdfs_http_request_handler(struct fdfs_http_context *pContext) 824 | { 825 | #define HTTPD_MAX_PARAMS 32 826 | char *file_id_without_group; 827 | char *url; 828 | char file_id[128]; 829 | char uri[512]; 830 | int url_len; 831 | int uri_len; 832 | int flv_header_len; 833 | int param_count; 834 | int ext_len; 835 | KeyValuePair params[HTTPD_MAX_PARAMS]; 836 | char *p; 837 | char *filename; 838 | const char *ext_name; 839 | FDFSStorePaths *pStorePaths; 840 | char true_filename[128]; 841 | char full_filename[MAX_PATH_SIZE + 64]; 842 | //char content_type[64]; 843 | struct stat file_stat; 844 | int64_t file_offset; 845 | int64_t file_size; 846 | int64_t download_bytes; 847 | int filename_len; 848 | int full_filename_len; 849 | int store_path_index; 850 | int fd; 851 | int result; 852 | int http_status; 853 | int the_storage_port; 854 | int i; 855 | struct fdfs_http_response response; 856 | FDFSFileInfo file_info; 857 | bool bFileExists; 858 | bool bSameGroup; //if in my group 859 | bool bTrunkFile; 860 | FDFSTrunkFullInfo trunkInfo; 861 | 862 | memset(&response, 0, sizeof(response)); 863 | response.status = HTTP_OK; 864 | 865 | //logInfo("url=%s", pContext->url); 866 | 867 | url_len = strlen(pContext->url); 868 | if (url_len < 16) 869 | { 870 | logError("file: "__FILE__", line: %d, " \ 871 | "url length: %d < 16", __LINE__, url_len); 872 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 873 | return HTTP_BADREQUEST; 874 | } 875 | 876 | if (strncasecmp(pContext->url, "http://", 7) == 0) 877 | { 878 | p = strchr(pContext->url + 7, '/'); 879 | if (p == NULL) 880 | { 881 | logError("file: "__FILE__", line: %d, " \ 882 | "invalid url: %s", __LINE__, pContext->url); 883 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 884 | return HTTP_BADREQUEST; 885 | } 886 | 887 | uri_len = url_len - (p - pContext->url); 888 | url = p; 889 | } 890 | else 891 | { 892 | uri_len = url_len; 893 | url = pContext->url; 894 | } 895 | 896 | if (uri_len + 1 >= (int)sizeof(uri)) 897 | { 898 | logError("file: "__FILE__", line: %d, " \ 899 | "uri length: %d is too long, >= %d", __LINE__, \ 900 | uri_len, (int)sizeof(uri)); 901 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 902 | return HTTP_BADREQUEST; 903 | } 904 | 905 | if (*url != '/') 906 | { 907 | *uri = '/'; 908 | memcpy(uri+1, url, uri_len+1); 909 | uri_len++; 910 | } 911 | else 912 | { 913 | memcpy(uri, url, uri_len+1); 914 | } 915 | 916 | the_storage_port = storage_server_port; 917 | param_count = http_parse_query(uri, params, HTTPD_MAX_PARAMS); 918 | if (url_have_group_name) 919 | { 920 | int group_name_len; 921 | 922 | fc_safe_strcpy(file_id, uri + 1); 923 | file_id_without_group = strchr(file_id, '/'); 924 | if (file_id_without_group == NULL) 925 | { 926 | logError("file: "__FILE__", line: %d, " \ 927 | "no group name in url, uri: %s", __LINE__, uri); 928 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 929 | return HTTP_BADREQUEST; 930 | } 931 | 932 | pStorePaths = &g_fdfs_store_paths; 933 | group_name_len = file_id_without_group - file_id; 934 | if (group_count == 0) 935 | { 936 | bSameGroup = (group_name_len == my_group_name_len) && \ 937 | (memcmp(file_id, my_group_name, \ 938 | group_name_len) == 0); 939 | } 940 | else 941 | { 942 | int i; 943 | 944 | bSameGroup = false; 945 | for (i=0; iget_request_header(pContext->arg, 999 | &bypass_header_name, &header_value); 1000 | if (val != NULL && fc_string_equal(&bypass_header_value, &header_value)) 1001 | { 1002 | need_check_token = false; 1003 | logDebug("file: " __FILE__", line: %d, " 1004 | "Skip check bcz found bypass_header, uri: %s", 1005 | __LINE__, uri); 1006 | } 1007 | else 1008 | { 1009 | need_check_token = true; 1010 | } 1011 | } 1012 | 1013 | if (need_check_token) 1014 | { 1015 | token = fdfs_http_get_parameter("token", params, param_count); 1016 | ts = fdfs_http_get_parameter("ts", params, param_count); 1017 | if (token == NULL || ts == NULL) 1018 | { 1019 | logError("file: "__FILE__", line: %d, " \ 1020 | "expect parameter token or ts in url, " \ 1021 | "uri: %s", __LINE__, uri); 1022 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 1023 | return HTTP_BADREQUEST; 1024 | } 1025 | 1026 | timestamp = atoi(ts); 1027 | if ((result=fdfs_http_check_token( \ 1028 | &g_http_params.anti_steal_secret_key, \ 1029 | file_id_without_group, timestamp, token, \ 1030 | g_http_params.token_ttl)) != 0) 1031 | { 1032 | logError("file: "__FILE__", line: %d, " \ 1033 | "check token fail, uri: %s, " \ 1034 | "errno: %d, error info: %s", \ 1035 | __LINE__, uri, result, STRERROR(result)); 1036 | if (*(g_http_params.token_check_fail_content_type)) 1037 | { 1038 | response.content_length = g_http_params. \ 1039 | token_check_fail_buff.length; 1040 | response.content_type = g_http_params. \ 1041 | token_check_fail_content_type; 1042 | OUTPUT_HEADERS(pContext, (&response), HTTP_OK); 1043 | 1044 | pContext->send_reply_chunk(pContext->arg, 1, \ 1045 | g_http_params.token_check_fail_buff.buff, 1046 | g_http_params.token_check_fail_buff.length); 1047 | 1048 | return HTTP_OK; 1049 | } 1050 | else 1051 | { 1052 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 1053 | return HTTP_BADREQUEST; 1054 | } 1055 | } 1056 | } 1057 | } 1058 | 1059 | filename = file_id_without_group; 1060 | filename_len = strlen(filename); 1061 | 1062 | //logInfo("filename=%s", filename); 1063 | if (storage_split_filename_no_check(filename, \ 1064 | &filename_len, true_filename, &store_path_index) != 0) 1065 | { 1066 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 1067 | return HTTP_BADREQUEST; 1068 | } 1069 | if (bSameGroup) 1070 | { 1071 | if (store_path_index < 0 || \ 1072 | store_path_index >= pStorePaths->count) 1073 | { 1074 | logError("file: "__FILE__", line: %d, " \ 1075 | "filename: %s is invalid, " \ 1076 | "invalid store path index: %d, " \ 1077 | "which < 0 or >= %d", __LINE__, filename, \ 1078 | store_path_index, pStorePaths->count); 1079 | 1080 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 1081 | return HTTP_BADREQUEST; 1082 | } 1083 | } 1084 | 1085 | if (fdfs_check_data_filename(true_filename, filename_len) != 0) 1086 | { 1087 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 1088 | return HTTP_BADREQUEST; 1089 | } 1090 | 1091 | if ((result=fdfs_get_file_info_ex1(file_id, false, &file_info, 1092 | FDFS_QUERY_FINFO_FLAGS_NOT_CALC_CRC32)) != 0) 1093 | { 1094 | if (result == ENOENT) 1095 | { 1096 | http_status = HTTP_NOTFOUND; 1097 | } 1098 | else if (result == EINVAL) 1099 | { 1100 | http_status = HTTP_BADREQUEST; 1101 | } 1102 | else 1103 | { 1104 | http_status = HTTP_INTERNAL_SERVER_ERROR; 1105 | } 1106 | 1107 | OUTPUT_HEADERS(pContext, (&response), http_status); 1108 | return http_status; 1109 | } 1110 | 1111 | if (file_info.file_size >= 0) //normal file 1112 | { 1113 | FDFS_SET_LAST_MODIFIED(response, pContext, \ 1114 | file_info.create_timestamp); 1115 | } 1116 | 1117 | fd = -1; 1118 | memset(&file_stat, 0, sizeof(file_stat)); 1119 | if (bSameGroup) 1120 | { 1121 | FDFSTrunkHeader trunkHeader; 1122 | if ((result=trunk_file_stat_ex1(pStorePaths, store_path_index, \ 1123 | true_filename, filename_len, &file_stat, \ 1124 | &trunkInfo, &trunkHeader, &fd)) != 0) 1125 | { 1126 | bFileExists = false; 1127 | } 1128 | else 1129 | { 1130 | bFileExists = true; 1131 | } 1132 | } 1133 | else 1134 | { 1135 | bFileExists = false; 1136 | memset(&trunkInfo, 0, sizeof(trunkInfo)); 1137 | } 1138 | 1139 | response.attachment_filename = fdfs_http_get_parameter("filename", \ 1140 | params, param_count); 1141 | if (bFileExists) 1142 | { 1143 | if (file_info.file_size < 0) //slave or appender file 1144 | { 1145 | FDFS_SET_LAST_MODIFIED(response, pContext, \ 1146 | file_stat.st_mtime); 1147 | } 1148 | } 1149 | else 1150 | { 1151 | char *redirect; 1152 | 1153 | //logInfo("source id: %d", file_info.source_id); 1154 | //logInfo("source ip addr: %s", file_info.source_ip_addr); 1155 | //logInfo("create_timestamp: %d", file_info.create_timestamp); 1156 | 1157 | if (bSameGroup && (is_local_host_ip(file_info.source_ip_addr) \ 1158 | || (file_info.create_timestamp > 0 && (time(NULL) - \ 1159 | file_info.create_timestamp > storage_sync_file_max_delay)))) 1160 | { 1161 | if (IS_TRUNK_FILE_BY_ID(trunkInfo)) 1162 | { 1163 | if (result == ENOENT) 1164 | { 1165 | logError("file: "__FILE__", line: %d, "\ 1166 | "logic file: %s not exist", \ 1167 | __LINE__, filename); 1168 | } 1169 | else 1170 | { 1171 | logError("file: "__FILE__", line: %d, "\ 1172 | "stat logic file: %s fail, " \ 1173 | "errno: %d, error info: %s", \ 1174 | __LINE__, filename, result, \ 1175 | STRERROR(result)); 1176 | } 1177 | } 1178 | else 1179 | { 1180 | fc_get_one_subdir_full_filename( 1181 | pStorePaths->paths[store_path_index].path.str, 1182 | pStorePaths->paths[store_path_index].path.len, 1183 | "data", 4, true_filename, filename_len, 1184 | full_filename); 1185 | if (result == ENOENT) 1186 | { 1187 | logError("file: "__FILE__", line: %d, "\ 1188 | "file: %s not exist", \ 1189 | __LINE__, full_filename); 1190 | } 1191 | else 1192 | { 1193 | logError("file: "__FILE__", line: %d, "\ 1194 | "stat file: %s fail, " \ 1195 | "errno: %d, error info: %s", \ 1196 | __LINE__, full_filename, \ 1197 | result, STRERROR(result)); 1198 | } 1199 | } 1200 | 1201 | OUTPUT_HEADERS(pContext, (&response), HTTP_NOTFOUND); 1202 | return HTTP_NOTFOUND; 1203 | } 1204 | 1205 | redirect = fdfs_http_get_parameter("redirect", 1206 | params, param_count); 1207 | if (redirect != NULL) 1208 | { 1209 | logWarning("file: "__FILE__", line: %d, " \ 1210 | "redirect again, url: %s", \ 1211 | __LINE__, url); 1212 | 1213 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 1214 | return HTTP_BADREQUEST; 1215 | } 1216 | 1217 | if (*(file_info.source_ip_addr) == '\0') 1218 | { 1219 | logWarning("file: "__FILE__", line: %d, " \ 1220 | "can't get ip address of source storage " \ 1221 | "id: %d, url: %s", __LINE__, \ 1222 | file_info.source_id, url); 1223 | 1224 | OUTPUT_HEADERS(pContext, (&response), HTTP_INTERNAL_SERVER_ERROR); 1225 | return HTTP_INTERNAL_SERVER_ERROR; 1226 | } 1227 | 1228 | if (response_mode == FDFS_MOD_REPONSE_MODE_REDIRECT) 1229 | { 1230 | char *path_split_str; 1231 | char port_part[16]; 1232 | char param_split_char; 1233 | 1234 | if (pContext->server_port == 80) 1235 | { 1236 | *port_part = '\0'; 1237 | } 1238 | else 1239 | { 1240 | *port_part = ':'; 1241 | fc_ltostr(pContext->server_port, port_part + 1); 1242 | } 1243 | 1244 | if (param_count == 0) 1245 | { 1246 | param_split_char = '?'; 1247 | } 1248 | else 1249 | { 1250 | param_split_char = '&'; 1251 | } 1252 | 1253 | if (*url != '/') 1254 | { 1255 | path_split_str = "/"; 1256 | } 1257 | else 1258 | { 1259 | path_split_str = ""; 1260 | } 1261 | 1262 | response.redirect_url_len = snprintf( \ 1263 | response.redirect_url, \ 1264 | sizeof(response.redirect_url), \ 1265 | "http://%s%s%s%s%c%s", \ 1266 | file_info.source_ip_addr, port_part, \ 1267 | path_split_str, url, \ 1268 | param_split_char, "redirect=1"); 1269 | 1270 | logDebug("file: "__FILE__", line: %d, " \ 1271 | "redirect to %s", \ 1272 | __LINE__, response.redirect_url); 1273 | 1274 | if (pContext->if_range) 1275 | { 1276 | fdfs_format_range(pContext, &response); 1277 | } 1278 | OUTPUT_HEADERS(pContext, (&response), HTTP_MOVETEMP); 1279 | return HTTP_MOVETEMP; 1280 | } 1281 | else if (pContext->proxy_handler != NULL) 1282 | { 1283 | return pContext->proxy_handler(pContext->arg, 1284 | file_info.source_ip_addr); 1285 | } 1286 | } 1287 | 1288 | ext_name = fdfs_http_get_file_extension(true_filename, 1289 | filename_len, &ext_len); 1290 | /* 1291 | if (g_http_params.need_find_content_type) 1292 | { 1293 | if (fdfs_http_get_content_type_by_extname(&g_http_params, \ 1294 | ext_name, ext_len, content_type, sizeof(content_type)) != 0) 1295 | { 1296 | if (fd >= 0) 1297 | { 1298 | close(fd); 1299 | } 1300 | OUTPUT_HEADERS(pContext, (&response), HTTP_SERVUNAVAIL); 1301 | return HTTP_SERVUNAVAIL; 1302 | } 1303 | response.content_type = content_type; 1304 | } 1305 | */ 1306 | 1307 | if (bFileExists) 1308 | { 1309 | file_size = file_stat.st_size; 1310 | } 1311 | else 1312 | { 1313 | bool if_get_file_info; 1314 | if_get_file_info = pContext->header_only || \ 1315 | (pContext->if_range && file_info.file_size < 0); 1316 | if (if_get_file_info) 1317 | { 1318 | if ((result=fdfs_get_file_info_ex1(file_id, true, &file_info, 1319 | FDFS_QUERY_FINFO_FLAGS_NOT_CALC_CRC32)) != 0) 1320 | { 1321 | if (result == ENOENT) 1322 | { 1323 | http_status = HTTP_NOTFOUND; 1324 | } 1325 | else 1326 | { 1327 | http_status = HTTP_INTERNAL_SERVER_ERROR; 1328 | } 1329 | 1330 | OUTPUT_HEADERS(pContext, (&response), http_status); 1331 | return http_status; 1332 | } 1333 | } 1334 | 1335 | file_size = file_info.file_size; 1336 | } 1337 | 1338 | flv_header_len = 0; 1339 | if (pContext->if_range) 1340 | { 1341 | if (fdfs_check_and_format_range(pContext, file_size) != 0 || 1342 | (pContext->range_count > 1 && !g_http_params.support_multi_range)) 1343 | { 1344 | if (fd >= 0) 1345 | { 1346 | close(fd); 1347 | } 1348 | 1349 | OUTPUT_HEADERS(pContext, (&response), HTTP_RANGE_NOT_SATISFIABLE); 1350 | return HTTP_RANGE_NOT_SATISFIABLE; 1351 | } 1352 | 1353 | if (pContext->range_count == 1) 1354 | { 1355 | download_bytes = FDFS_RANGE_LENGTH(pContext->ranges[0]); 1356 | } 1357 | else 1358 | { 1359 | download_bytes = fdfs_calc_download_bytes(pContext); 1360 | } 1361 | fdfs_format_content_range(pContext, file_size, &response); 1362 | } 1363 | else 1364 | { 1365 | download_bytes = file_size > 0 ? file_size : 0; 1366 | 1367 | //flv support 1368 | if (flv_support && (flv_ext_len == ext_len && \ 1369 | memcmp(ext_name, flv_extension, ext_len) == 0)) 1370 | { 1371 | char *pStart; 1372 | pStart = fdfs_http_get_parameter("start", \ 1373 | params, param_count); 1374 | if (pStart != NULL) 1375 | { 1376 | int64_t start; 1377 | if (fdfs_strtoll(pStart, &start) == 0) 1378 | { 1379 | char *pEnd; 1380 | 1381 | pContext->range_count = 1; 1382 | pContext->ranges[0].start = start; 1383 | pContext->ranges[0].end = 0; 1384 | pEnd = fdfs_http_get_parameter("end", \ 1385 | params, param_count); 1386 | if (pEnd != NULL) 1387 | { 1388 | int64_t end; 1389 | if (fdfs_strtoll(pEnd, &end) == 0) 1390 | { 1391 | pContext->ranges[0].end = end - 1; 1392 | } 1393 | } 1394 | 1395 | if (fdfs_check_and_format_range(pContext, file_size) != 0) 1396 | { 1397 | if (fd >= 0) 1398 | { 1399 | close(fd); 1400 | } 1401 | 1402 | OUTPUT_HEADERS(pContext, (&response), HTTP_BADREQUEST); 1403 | return HTTP_BADREQUEST; 1404 | } 1405 | 1406 | download_bytes = FDFS_RANGE_LENGTH(pContext->ranges[0]); 1407 | if (start > 0) 1408 | { 1409 | flv_header_len = sizeof(flv_header) - 1; 1410 | } 1411 | } 1412 | } 1413 | } 1414 | } 1415 | 1416 | //logInfo("flv_header_len: %d", flv_header_len); 1417 | 1418 | if (pContext->header_only) 1419 | { 1420 | if (fd >= 0) 1421 | { 1422 | close(fd); 1423 | } 1424 | 1425 | if (fdfs_calc_content_length(pContext, download_bytes, flv_header_len, 1426 | ext_name, ext_len, &response) != 0) 1427 | { 1428 | OUTPUT_HEADERS(pContext, (&response), HTTP_SERVUNAVAIL); 1429 | return HTTP_SERVUNAVAIL; 1430 | } 1431 | OUTPUT_HEADERS(pContext, (&response), pContext->if_range ? \ 1432 | HTTP_PARTIAL_CONTENT : HTTP_OK ); 1433 | 1434 | return HTTP_OK; 1435 | } 1436 | 1437 | if (fdfs_calc_content_length(pContext, download_bytes, flv_header_len, 1438 | ext_name, ext_len, &response) != 0) 1439 | { 1440 | OUTPUT_HEADERS(pContext, (&response), HTTP_SERVUNAVAIL); 1441 | return HTTP_SERVUNAVAIL; 1442 | } 1443 | 1444 | if (!bFileExists) 1445 | { 1446 | ConnectionInfo storage_server; 1447 | struct fdfs_download_callback_args callback_args; 1448 | int64_t file_size; 1449 | 1450 | strcpy(storage_server.ip_addr, file_info.source_ip_addr); 1451 | storage_server.port = the_storage_port; 1452 | storage_server.sock = -1; 1453 | 1454 | callback_args.pContext = pContext; 1455 | callback_args.pResponse = &response; 1456 | callback_args.sent_bytes = 0; 1457 | callback_args.range_index = 0; 1458 | 1459 | if (pContext->if_range) 1460 | { 1461 | download_bytes = FDFS_RANGE_LENGTH(pContext->ranges[0]); 1462 | } 1463 | result = storage_download_file_ex1(NULL, \ 1464 | &storage_server, file_id, \ 1465 | pContext->ranges[0].start, download_bytes, \ 1466 | fdfs_download_callback, &callback_args, &file_size); 1467 | 1468 | logDebug("file: "__FILE__", line: %d, " \ 1469 | "storage_download_file_ex1 return code: %d, " \ 1470 | "file id: %s", __LINE__, result, file_id); 1471 | 1472 | if (result == 0) 1473 | { 1474 | http_status = HTTP_OK; 1475 | } 1476 | if (result == ENOENT) 1477 | { 1478 | http_status = HTTP_NOTFOUND; 1479 | } 1480 | else 1481 | { 1482 | http_status = HTTP_INTERNAL_SERVER_ERROR; 1483 | } 1484 | 1485 | OUTPUT_HEADERS(pContext, (&response), http_status); 1486 | 1487 | if (result != 0 || !(pContext->if_range && pContext->range_count > 1)) 1488 | { 1489 | return http_status; 1490 | } 1491 | 1492 | for (i=1; irange_count; i++) 1493 | { 1494 | callback_args.sent_bytes = 0; 1495 | callback_args.range_index = i; 1496 | 1497 | download_bytes = FDFS_RANGE_LENGTH(pContext->ranges[i]); 1498 | result = storage_download_file_ex1(NULL, 1499 | &storage_server, file_id, 1500 | pContext->ranges[i].start, download_bytes, 1501 | fdfs_download_callback, &callback_args, &file_size); 1502 | if (result != 0) 1503 | { 1504 | return HTTP_INTERNAL_SERVER_ERROR; 1505 | } 1506 | } 1507 | 1508 | if (fdfs_send_boundary(pContext, &response, true) != 0) 1509 | { 1510 | return HTTP_INTERNAL_SERVER_ERROR; 1511 | } 1512 | 1513 | return http_status; 1514 | } 1515 | 1516 | bTrunkFile = IS_TRUNK_FILE_BY_ID(trunkInfo); 1517 | if (bTrunkFile) 1518 | { 1519 | trunk_get_full_filename_ex(pStorePaths, &trunkInfo, \ 1520 | full_filename, sizeof(full_filename)); 1521 | full_filename_len = strlen(full_filename); 1522 | file_offset = TRUNK_FILE_START_OFFSET(trunkInfo) + \ 1523 | pContext->ranges[0].start; 1524 | } 1525 | else 1526 | { 1527 | full_filename_len = fc_get_one_subdir_full_filename( 1528 | pStorePaths->paths[store_path_index].path.str, 1529 | pStorePaths->paths[store_path_index].path.len, 1530 | "data", 4, true_filename, filename_len, 1531 | full_filename); 1532 | file_offset = pContext->ranges[0].start; 1533 | } 1534 | 1535 | if (pContext->send_file != NULL && !bTrunkFile && 1536 | !(pContext->if_range && pContext->range_count > 1)) 1537 | { 1538 | http_status = pContext->if_range ? \ 1539 | HTTP_PARTIAL_CONTENT : HTTP_OK; 1540 | OUTPUT_HEADERS(pContext, (&response), http_status); 1541 | 1542 | if (flv_header_len > 0) 1543 | { 1544 | if (pContext->send_reply_chunk(pContext->arg, \ 1545 | false, flv_header, flv_header_len) != 0) 1546 | { 1547 | close(fd); 1548 | return HTTP_INTERNAL_SERVER_ERROR; 1549 | } 1550 | } 1551 | 1552 | return pContext->send_file(pContext->arg, full_filename, \ 1553 | full_filename_len, file_offset, download_bytes); 1554 | } 1555 | 1556 | if (fd < 0) 1557 | { 1558 | fd = open(full_filename, O_RDONLY); 1559 | if (fd < 0) 1560 | { 1561 | logError("file: "__FILE__", line: %d, " \ 1562 | "open file %s fail, " \ 1563 | "errno: %d, error info: %s", __LINE__, \ 1564 | full_filename, errno, STRERROR(errno)); 1565 | OUTPUT_HEADERS(pContext, (&response), \ 1566 | HTTP_SERVUNAVAIL); 1567 | return HTTP_SERVUNAVAIL; 1568 | } 1569 | if (file_offset > 0 && lseek(fd, file_offset, SEEK_SET) < 0) 1570 | { 1571 | close(fd); 1572 | logError("file: "__FILE__", line: %d, " \ 1573 | "lseek file: %s fail, " \ 1574 | "errno: %d, error info: %s", \ 1575 | __LINE__, full_filename, \ 1576 | errno, STRERROR(errno)); 1577 | OUTPUT_HEADERS(pContext, (&response), HTTP_INTERNAL_SERVER_ERROR); 1578 | return HTTP_INTERNAL_SERVER_ERROR; 1579 | } 1580 | } 1581 | else 1582 | { 1583 | if (pContext->ranges[0].start > 0 && \ 1584 | lseek(fd, pContext->ranges[0].start, SEEK_CUR) < 0) 1585 | { 1586 | close(fd); 1587 | logError("file: "__FILE__", line: %d, " \ 1588 | "lseek file: %s fail, " \ 1589 | "errno: %d, error info: %s", \ 1590 | __LINE__, full_filename, \ 1591 | errno, STRERROR(errno)); 1592 | OUTPUT_HEADERS(pContext, (&response), HTTP_INTERNAL_SERVER_ERROR); 1593 | return HTTP_INTERNAL_SERVER_ERROR; 1594 | } 1595 | } 1596 | 1597 | OUTPUT_HEADERS(pContext, (&response), pContext->if_range ? \ 1598 | HTTP_PARTIAL_CONTENT : HTTP_OK); 1599 | if (pContext->if_range && pContext->range_count > 1) 1600 | { 1601 | if (fdfs_send_boundary(pContext, &response, false) != 0) 1602 | { 1603 | close(fd); 1604 | return HTTP_INTERNAL_SERVER_ERROR; 1605 | } 1606 | if (fdfs_send_range_subheader(pContext, &response, 0) != 0) 1607 | { 1608 | close(fd); 1609 | return HTTP_INTERNAL_SERVER_ERROR; 1610 | } 1611 | } 1612 | 1613 | if (flv_header_len > 0) 1614 | { 1615 | if (pContext->send_reply_chunk(pContext->arg, \ 1616 | false, flv_header, flv_header_len) != 0) 1617 | { 1618 | close(fd); 1619 | return HTTP_INTERNAL_SERVER_ERROR; 1620 | } 1621 | } 1622 | 1623 | if (pContext->if_range) 1624 | { 1625 | download_bytes = FDFS_RANGE_LENGTH(pContext->ranges[0]); 1626 | } 1627 | if (fdfs_send_file_buffer(pContext, full_filename, fd, download_bytes, 1628 | !(pContext->if_range && pContext->range_count > 1)) != 0) 1629 | { 1630 | close(fd); 1631 | return HTTP_INTERNAL_SERVER_ERROR; 1632 | } 1633 | 1634 | if (!(pContext->if_range && pContext->range_count > 1)) 1635 | { 1636 | close(fd); 1637 | return HTTP_OK; 1638 | } 1639 | 1640 | for (i=1; irange_count; i++) 1641 | { 1642 | if (bTrunkFile) 1643 | { 1644 | file_offset = TRUNK_FILE_START_OFFSET(trunkInfo) + 1645 | pContext->ranges[i].start; 1646 | } 1647 | else 1648 | { 1649 | file_offset = pContext->ranges[i].start; 1650 | } 1651 | 1652 | if (lseek(fd, file_offset, SEEK_SET) < 0) 1653 | { 1654 | close(fd); 1655 | logError("file: "__FILE__", line: %d, " \ 1656 | "lseek file: %s fail, " \ 1657 | "errno: %d, error info: %s", \ 1658 | __LINE__, full_filename, \ 1659 | errno, STRERROR(errno)); 1660 | return HTTP_INTERNAL_SERVER_ERROR; 1661 | } 1662 | 1663 | if (fdfs_send_boundary(pContext, &response, false) != 0) 1664 | { 1665 | close(fd); 1666 | return HTTP_INTERNAL_SERVER_ERROR; 1667 | } 1668 | if (fdfs_send_range_subheader(pContext, &response, i) != 0) 1669 | { 1670 | close(fd); 1671 | return HTTP_INTERNAL_SERVER_ERROR; 1672 | } 1673 | 1674 | if (fdfs_send_file_buffer(pContext, full_filename, fd, 1675 | FDFS_RANGE_LENGTH(pContext->ranges[i]), false) != 0) 1676 | { 1677 | close(fd); 1678 | return HTTP_INTERNAL_SERVER_ERROR; 1679 | } 1680 | } 1681 | 1682 | close(fd); 1683 | if (fdfs_send_boundary(pContext, &response, true) != 0) 1684 | { 1685 | return HTTP_INTERNAL_SERVER_ERROR; 1686 | } 1687 | 1688 | return HTTP_OK; 1689 | } 1690 | 1691 | static int fdfs_get_params_from_tracker() 1692 | { 1693 | IniContext iniContext; 1694 | int result; 1695 | bool continue_flag; 1696 | 1697 | continue_flag = false; 1698 | if ((result=fdfs_get_ini_context_from_tracker(&g_tracker_group, 1699 | &iniContext, &continue_flag)) != 0) 1700 | { 1701 | return result; 1702 | } 1703 | 1704 | storage_sync_file_max_delay = iniGetIntValue(NULL, \ 1705 | "storage_sync_file_max_delay", \ 1706 | &iniContext, 24 * 3600); 1707 | 1708 | use_storage_id = iniGetBoolValue(NULL, "use_storage_id", \ 1709 | &iniContext, false); 1710 | iniFreeContext(&iniContext); 1711 | 1712 | if (use_storage_id) 1713 | { 1714 | result = fdfs_get_storage_ids_from_tracker_group( \ 1715 | &g_tracker_group); 1716 | } 1717 | 1718 | return result; 1719 | } 1720 | 1721 | static int fdfs_format_http_datetime(time_t t, char *buff, const int buff_size) 1722 | { 1723 | struct tm tm; 1724 | struct tm *ptm; 1725 | 1726 | *buff = '\0'; 1727 | if ((ptm=gmtime_r(&t, &tm)) == NULL) 1728 | { 1729 | return errno != 0 ? errno : EFAULT; 1730 | } 1731 | 1732 | strftime(buff, buff_size, "%a, %d %b %Y %H:%M:%S GMT", ptm); 1733 | return 0; 1734 | } 1735 | 1736 | static int fdfs_parse_range(char *value, struct fdfs_http_range *range) 1737 | { 1738 | int result; 1739 | char *pEndPos; 1740 | 1741 | if (*value == '-') 1742 | { 1743 | if ((result=fdfs_strtoll(value, &(range->start))) != 0) 1744 | { 1745 | return result; 1746 | } 1747 | range->end = 0; 1748 | return 0; 1749 | } 1750 | 1751 | pEndPos = strchr(value, '-'); 1752 | if (pEndPos == NULL) 1753 | { 1754 | return EINVAL; 1755 | } 1756 | 1757 | *pEndPos = '\0'; 1758 | if ((result=fdfs_strtoll(value, &(range->start))) != 0) 1759 | { 1760 | return result; 1761 | } 1762 | 1763 | pEndPos++; //skip - 1764 | if (*pEndPos == '\0') 1765 | { 1766 | range->end = 0; 1767 | } 1768 | else 1769 | { 1770 | if ((result=fdfs_strtoll(pEndPos, &(range->end))) != 0) 1771 | { 1772 | return result; 1773 | } 1774 | } 1775 | 1776 | return 0; 1777 | } 1778 | 1779 | int fdfs_parse_ranges(const char *value, struct fdfs_http_context *pContext) 1780 | { 1781 | /* 1782 | range format: 1783 | bytes=500-999 1784 | bytes=-500 1785 | bytes=9500- 1786 | */ 1787 | #define RANGE_PREFIX_STR "bytes=" 1788 | #define RANGE_PREFIX_LEN (int)(sizeof(RANGE_PREFIX_STR) - 1) 1789 | 1790 | int result; 1791 | int len; 1792 | int i; 1793 | const char *p; 1794 | char buff[256]; 1795 | char *parts[FDFS_MAX_HTTP_RANGES]; 1796 | 1797 | len = strlen(value); 1798 | if (len <= RANGE_PREFIX_LEN + 1) 1799 | { 1800 | return EINVAL; 1801 | } 1802 | 1803 | p = value + RANGE_PREFIX_LEN; 1804 | len -= RANGE_PREFIX_LEN; 1805 | if (len >= (int)sizeof(buff)) 1806 | { 1807 | return EINVAL; 1808 | } 1809 | memcpy(buff, p, len); 1810 | *(buff + len) = '\0'; 1811 | 1812 | result = 0; 1813 | pContext->range_count = splitEx(buff, ',', parts, FDFS_MAX_HTTP_RANGES); 1814 | for (i=0; irange_count; i++) 1815 | { 1816 | if ((result=fdfs_parse_range(parts[i], pContext->ranges + i)) != 0) 1817 | { 1818 | break; 1819 | } 1820 | } 1821 | 1822 | return result; 1823 | } 1824 | --------------------------------------------------------------------------------