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
+ }
0 commit comments