Skip to content

Commit 1c469fe

Browse files
committed
http module support range download; add http download example
1 parent 1c43c88 commit 1c469fe

29 files changed

+2428
-29
lines changed
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
include ./Makefile.in
2+
ifeq ($(findstring FreeBSD, $(UNIXNAME)), FreeBSD)
3+
EXTLIBS += -L/usr/local/lib -liconv
4+
endif
5+
PROG = httpd_download
+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
CC = g++
2+
3+
CFLAGS = -c -g -W -Wall -Wcast-qual -Wcast-align \
4+
-Waggregate-return -Wno-long-long \
5+
-Wpointer-arith -Werror -Wshadow -O3 \
6+
-D_REENTRANT -D_POSIX_PTHREAD_SEMANTICS -D_USE_FAST_MACRO
7+
8+
###########################################################
9+
#Check system:
10+
# Linux, SunOS, Solaris, BSD variants, AIX, HP-UX
11+
SYSLIB = -lpthread -lcrypt -lz
12+
CHECKSYSRES = @echo "Unknow system type!";exit 1
13+
UNIXNAME = $(shell uname -sm)
14+
OSTYPE = $(shell uname -p)
15+
RPATH = linux64
16+
17+
ifeq ($(CC),)
18+
CC = gcc
19+
endif
20+
21+
# For FreeBSD
22+
ifeq ($(findstring FreeBSD, $(UNIXNAME)), FreeBSD)
23+
ifeq ($(findstring gcc, $(CC)), gcc)
24+
CFLAGS += -Wstrict-prototypes
25+
endif
26+
CFLAGS += -DFREEBSD -D_REENTRANT
27+
SYSLIB = -lcrypt -lpthread -lz
28+
RPATH = freebsd
29+
endif
30+
31+
#Path for Linux
32+
ifeq ($(findstring Linux, $(UNIXNAME)), Linux)
33+
ifeq ($CC, "gcc")
34+
CFLAGS += -Wstrict-prototypes
35+
endif
36+
ifeq ($(findstring i686, $(OSTYPE)), i686)
37+
RPATH = linux32
38+
endif
39+
ifeq ($(findstring x86_64, $(OSTYPE)), x86_64)
40+
RPATH = linux64
41+
endif
42+
CFLAGS += -DLINUX2 -D_REENTRANT
43+
endif
44+
45+
#Path for SunOS
46+
ifeq ($(findstring SunOS, $(UNIXNAME)), SunOS)
47+
ifeq ($(findstring 86, $(UNIXNAME)), 86)
48+
SYSLIB += -lsocket -lnsl -lrt
49+
endif
50+
ifeq ($(findstring sun4u, $(UNIXNAME)), sun4u)
51+
SYSLIB += -lsocket -lnsl -lrt
52+
endif
53+
ifeq ($CC, "gcc")
54+
CFLAGS += -Wstrict-prototypes
55+
endif
56+
CFLAGS += -DSUNOS5 -D_REENTRANT
57+
RPATH = sunos_x86
58+
endif
59+
60+
#Path for HP-UX
61+
ifeq ($(findstring HP-UX, $(UNIXNAME)), HP-UX)
62+
ifeq ($CC, "gcc")
63+
CFLAGS += -Wstrict-prototypes
64+
endif
65+
CFLAGS += -DHP_UX -DHPUX11
66+
PLAT_NAME=hp-ux
67+
endif
68+
69+
#Find system type.
70+
ifneq ($(SYSPATH),)
71+
CHECKSYSRES = @echo "System is $(shell uname -sm)"
72+
endif
73+
###########################################################
74+
75+
CFLAGS += -I../../../lib_acl/include -I../../../lib_protocol/include -I../../../lib_acl_cpp/include
76+
EXTLIBS =
77+
LDFLAGS = -L../../../lib_acl_cpp/lib -l_acl_cpp -L../../../lib_protocol/lib -l_protocol -L../../../lib_acl/lib -l_acl \
78+
$(EXTLIBS) $(SYSLIB)
79+
80+
COMPILE = $(CC) $(CFLAGS)
81+
LINK = $(CC) $(OBJ) $(LDFLAGS)
82+
###########################################################
83+
OBJ_PATH = .
84+
85+
#Project's objs
86+
SRC = $(wildcard *.cpp)
87+
OBJ = $(patsubst %.cpp, $(OBJ_PATH)/%.o, $(notdir $(SRC)))
88+
89+
$(OBJ_PATH)/%.o: %.cpp
90+
$(COMPILE) $< -o $@
91+
92+
.PHONY = all clean
93+
all: RM $(OBJ)
94+
$(LINK) -o $(PROG)
95+
@echo ""
96+
@echo "All ok! Output:$(PROG)"
97+
@echo ""
98+
RM:
99+
rm -f $(PROG)
100+
clean:
101+
rm -f $(PROG)
102+
rm -f $(OBJ)
103+
install:
104+
cp $(PROG) ../../../dist/master/libexec/$(RPATH)/
105+
cp $(PROG).cf ../../../dist/master/conf/service/
106+
###########################################################
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
#include "stdafx.h"
2+
#include "http_servlet.h"
3+
4+
http_servlet::http_servlet(const char* filepath)
5+
{
6+
if (filepath && *filepath)
7+
filepath_ = filepath;
8+
}
9+
10+
http_servlet::~http_servlet(void)
11+
{
12+
13+
}
14+
15+
bool http_servlet::reply(acl::HttpServletRequest& req,
16+
acl::HttpServletResponse& res, int status, const char* fmt, ...)
17+
{
18+
va_list ap;
19+
va_start(ap, fmt);
20+
acl::string buf;
21+
buf.vformat(fmt, ap);
22+
va_end(ap);
23+
bool keep_alive = req.isKeepAlive();
24+
res.setStatus(status)
25+
.setKeepAlive(keep_alive)
26+
.setContentType("text/html; charset=utf-8")
27+
.setContentLength(buf.length());
28+
return res.write(buf) && keep_alive;
29+
}
30+
31+
bool http_servlet::doError(acl::HttpServletRequest& req,
32+
acl::HttpServletResponse& res)
33+
{
34+
acl::string hdr;
35+
req.getClient()->sprint_header(hdr);
36+
logger("error request head:\r\n%s\r\n", hdr.c_str());
37+
38+
(void) reply(req, res, 400, "unknown request method\r\n");
39+
return false;
40+
}
41+
42+
bool http_servlet::doUnknown(acl::HttpServletRequest& req,
43+
acl::HttpServletResponse& res)
44+
{
45+
acl::string hdr;
46+
req.getClient()->sprint_header(hdr);
47+
logger("request head:\r\n%s\r\n", hdr.c_str());
48+
49+
(void) reply(req, res, 400, "unknown request method\r\n");
50+
return false;
51+
}
52+
53+
bool http_servlet::doGet(acl::HttpServletRequest& req,
54+
acl::HttpServletResponse& res)
55+
{
56+
return doPost(req, res);
57+
}
58+
59+
bool http_servlet::doPost(acl::HttpServletRequest& req,
60+
acl::HttpServletResponse& res)
61+
{
62+
// 如果需要 http session 控制,请打开下面注释,且需要保证
63+
// 在 master_service.cpp 的函数 thread_on_read 中设置的
64+
// memcached 服务正常工作
65+
/*
66+
const char* sid = req.getSession().getAttribute("sid");
67+
if (*sid == 0)
68+
req.getSession().setAttribute("sid", "xxxxxx");
69+
sid = req.getSession().getAttribute("sid");
70+
*/
71+
acl::string hdr;
72+
req.getClient()->sprint_header(hdr);
73+
logger("request head:\r\n%s\r\n", hdr.c_str());
74+
75+
long long int range_from, range_to;
76+
if (req.getRange(range_from, range_to) == false)
77+
return transfer_file(req, res);
78+
else
79+
return transfer_file(req, res, range_from, range_to);
80+
}
81+
82+
bool http_servlet::transfer_file(acl::HttpServletRequest& req,
83+
acl::HttpServletResponse& res)
84+
{
85+
acl::ifstream in;
86+
87+
// 只读方式打开文件流
88+
if (in.open_read(filepath_) == false)
89+
{
90+
logger_error("open file %s error %s", filepath_.c_str(),
91+
acl::last_serror());
92+
return reply(req, res, 500, "open %s error %s",
93+
filepath_.c_str(), acl::last_serror());
94+
}
95+
96+
long long int fsize = in.fsize();
97+
if (fsize <= 0)
98+
return reply(req, res, 500, "invalid file size: %lld", fsize);
99+
100+
bool keep_alive = req.isKeepAlive();
101+
102+
// 设置 HTTP 响应头中的字段
103+
res.setStatus(200)
104+
.setKeepAlive(keep_alive)
105+
.setContentLength(fsize)
106+
.setContentType("application/octet-stream");
107+
108+
acl::string hdr;
109+
res.getHttpHeader().build_response(hdr);
110+
logger("response head:\r\n%s\r\n", hdr.c_str());
111+
112+
long long n = 0;
113+
char buf[8192];
114+
115+
// 从文件流中读取数据并将数据发给客户端
116+
while (!in.eof())
117+
{
118+
int ret = in.read(buf, sizeof(buf), false);
119+
if (ret == -1)
120+
break;
121+
if (res.write(buf, ret) == false)
122+
return false;
123+
n += ret;
124+
//acl_doze(100); // 休息 100 ms 便于测试
125+
}
126+
127+
return n == fsize ? true : false;
128+
}
129+
130+
// 支持断点续传的数据传输过程
131+
bool http_servlet::transfer_file(acl::HttpServletRequest& req,
132+
acl::HttpServletResponse& res, long long range_from, long long range_to)
133+
{
134+
if (range_from < 0)
135+
return reply(req, res, 400, "invalid range_from: %lld",
136+
range_from);
137+
if (range_to > 0 && range_to < range_from)
138+
return reply(req, res, 400, "range_from(%lld) > range_to(%lld)",
139+
range_from, range_to);
140+
141+
long long length;
142+
if (range_to >= range_from)
143+
length = range_to - range_from + 1;
144+
else
145+
length = -1;
146+
147+
acl::ifstream in;
148+
// 只读方式打开文件流
149+
if (in.open_read(filepath_) == false)
150+
{
151+
logger_error("open file %s error %s", filepath_.c_str(),
152+
acl::last_serror());
153+
return reply(req, res, 500, "open %s error %s",
154+
filepath_.c_str(), acl::last_serror());
155+
}
156+
157+
long long int fsize = in.fsize();
158+
if (fsize <= 0)
159+
return reply(req, res, 500, "invalid file size: %lld", fsize);
160+
161+
// 如果偏移位置超过了文件总长度,则返回错误
162+
if (range_to >= fsize)
163+
return reply(req, res, 400, "range_to(%lld) >= fsize(%lld)",
164+
range_to, fsize);
165+
166+
// 如果客户端要求从指定偏移位置至文件尾,则重新计算需要读取的数据长度,
167+
// 此值作为实际要传输给客户端的长度
168+
if (length == -1)
169+
length = fsize - range_from;
170+
171+
// 定位文件偏移位置
172+
if (in.fseek(range_from, SEEK_SET) < 0)
173+
return reply(req, res, 500, "fseek(%lld) error %s",
174+
range_from, acl::last_serror());
175+
176+
bool keep_alive = req.isKeepAlive();
177+
178+
// 设置 HTTP 响应头中的字段
179+
res.setStatus(206) // 响应状态必须为 206
180+
.setKeepAlive(keep_alive) // 是否保持长连接
181+
.setContentLength(length) // 实际要传输的数据长度
182+
.setContentType("application/octet-stream"); // 数据类型
183+
184+
// 设置分段传输数据的范围
185+
res.getHttpHeader()
186+
// 设置本次传输区间
187+
.set_range(range_from, range_to > 0 ? range_to : fsize - 1)
188+
// 设置数据总长度
189+
.set_range_total(fsize);
190+
191+
acl::string hdr;
192+
res.getHttpHeader().build_response(hdr);
193+
logger("response head:\r\n%s\r\n", hdr.c_str());
194+
195+
char buf[8192];
196+
int ret;
197+
size_t size;
198+
199+
while (!in.eof() && length > 0)
200+
{
201+
size = sizeof(buf) > length ? (size_t) length : sizeof(buf);
202+
ret = in.read(buf, size, false);
203+
if (ret == -1)
204+
{
205+
printf("read over: %s\r\n", acl::last_serror());
206+
break;
207+
}
208+
if (res.write(buf, ret) == false)
209+
return false;
210+
length -= ret;
211+
//acl_doze(100); // 休息 100 ms 便于测试
212+
}
213+
214+
if (length != 0)
215+
return false;
216+
return true;
217+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#pragma once
2+
3+
class http_servlet : public acl::HttpServlet
4+
{
5+
public:
6+
http_servlet(const char* filepath);
7+
~http_servlet();
8+
9+
protected:
10+
virtual bool doError(acl::HttpServletRequest& req,
11+
acl::HttpServletResponse& res);
12+
virtual bool doUnknown(acl::HttpServletRequest&,
13+
acl::HttpServletResponse& res);
14+
virtual bool doGet(acl::HttpServletRequest& req,
15+
acl::HttpServletResponse& res);
16+
virtual bool doPost(acl::HttpServletRequest& req,
17+
acl::HttpServletResponse& res);
18+
19+
private:
20+
acl::string filepath_;
21+
bool reply(acl::HttpServletRequest& req,
22+
acl::HttpServletResponse& res, int status,
23+
const char* fmt, ...);
24+
bool transfer_file(acl::HttpServletRequest& req,
25+
acl::HttpServletResponse& res);
26+
bool transfer_file(acl::HttpServletRequest& req,
27+
acl::HttpServletResponse& res,
28+
long long range_from, long long range_to);
29+
};

0 commit comments

Comments
 (0)