btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / poorman / PoorManServer.cpp
blobfaace730e4964f15861dbdf9205e935adcf7b693
1 /*
2 * Copyright 2009 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Author(s):
6 * Ma Jie, china.majie at gmail
7 */
9 #include "PoorManServer.h"
11 #include <pthread.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <time.h> //for struct timeval
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
19 #include <File.h>
20 #include <Debug.h>
21 #include <OS.h>
22 #include <String.h>
23 #include <StorageDefs.h>
24 #include <SupportDefs.h>
26 #include "PoorManApplication.h"
27 #include "PoorManLogger.h"
28 #include "PoorManWindow.h"
29 #include "libhttpd/libhttpd.h"
32 PoorManServer::PoorManServer(const char* webDir,
33 int32 maxConns, bool listDir,const char* idxName)
34 :fIsRunning(false),
35 fMaxConns(maxConns),
36 fIndexName(new char[strlen(idxName) + 1]),
37 fCurConns(0)
39 fHttpdServer = httpd_initialize(
40 (char*)0,//hostname
41 (httpd_sockaddr*)0,//sa4P
42 (httpd_sockaddr*)0,//sa6P
43 (unsigned short)80,//port
44 (char*)0,//cgi pattern
45 0,//cgi_limit
46 (char *)"iso-8859-1",//charset
47 (char *)"",//p3p
48 -1,//max_age
49 const_cast<char*>(webDir),//cwd
50 1,//no_log
51 (FILE*)0,//logfp
52 0,//no_symlink_check
53 0,//vhost
54 0,//global_passwd
55 (char*)0,//url_pattern
56 (char*)0,//local_pattern
57 0//no_empty_referers
60 strcpy(fIndexName, idxName);
62 size_t cwdLen = strlen(fHttpdServer->cwd);
63 if (fHttpdServer->cwd[cwdLen-1] == '/') {
64 fHttpdServer->cwd[cwdLen-1] = '\0';
67 fHttpdServer->do_list_dir = (listDir ? 1 : 0);
68 fHttpdServer->index_name = fIndexName;
70 pthread_rwlock_init(&fWebDirLock, NULL);
71 pthread_rwlock_init(&fIndexNameLock, NULL);
75 PoorManServer::~PoorManServer()
77 Stop();
78 httpd_terminate(fHttpdServer);
79 delete[] fIndexName;
80 pthread_rwlock_destroy(&fWebDirLock);
81 pthread_rwlock_destroy(&fIndexNameLock);
85 status_t PoorManServer::Run()
87 if (chdir(fHttpdServer->cwd) == -1) {
88 poorman_log("no web directory, can't start up.\n", false, INADDR_NONE, RED);
89 return B_ERROR;
92 httpd_sockaddr sa4;
93 memset(&sa4, 0, sizeof(httpd_sockaddr));
94 sa4.sa_in.sin_family = AF_INET;
95 sa4.sa_in.sin_port = htons(80);
96 sa4.sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
97 fHttpdServer->listen4_fd = httpd_initialize_listen_socket(&sa4);
98 if (fHttpdServer->listen4_fd == -1)
99 return B_ERROR;
101 fListenerTid = spawn_thread(
102 PoorManServer::_Listener,
103 "www listener",
104 B_NORMAL_PRIORITY,
105 static_cast<void*>(this)
107 if (fListenerTid < B_OK) {
108 poorman_log("can't create listener thread.\n", false, INADDR_NONE, RED);
109 return B_ERROR;
111 fIsRunning = true;
112 if (resume_thread(fListenerTid) != B_OK) {
113 fIsRunning = false;
114 return B_ERROR;
117 //our server is up and running
118 return B_OK;
122 status_t PoorManServer::Stop()
124 if (fIsRunning) {
125 fIsRunning = false;
126 httpd_unlisten(fHttpdServer);
128 return B_OK;
132 /*The Web Dir is not changed if an error occured.
134 status_t PoorManServer::SetWebDir(const char* webDir)
136 if (chdir(webDir) == -1) {
137 //log it
138 return B_ERROR;
141 char* tmp = strdup(webDir);
142 if (tmp == NULL)
143 return B_ERROR;
145 if (pthread_rwlock_wrlock(&fWebDirLock) == 0) {
146 free(fHttpdServer->cwd);
147 fHttpdServer->cwd = tmp;
148 if (tmp[strlen(tmp) - 1] == '/') {
149 tmp[strlen(tmp) - 1] = '\0';
151 pthread_rwlock_unlock(&fWebDirLock);
152 } else {
153 free(tmp);
154 return B_ERROR;
157 return B_OK;
161 status_t PoorManServer::SetMaxConns(int32 count)
163 fMaxConns = count;
164 return B_OK;
168 status_t PoorManServer::SetListDir(bool listDir)
170 fHttpdServer->do_list_dir = (listDir ? 1 : 0);
171 return B_OK;
175 status_t PoorManServer::SetIndexName(const char* idxName)
177 size_t length = strlen(idxName);
178 if (length > B_PATH_NAME_LENGTH + 1)
179 return B_ERROR;
181 char* tmp = new char[length + 1];
182 if (tmp == NULL)
183 return B_ERROR;
185 strcpy(tmp, idxName);
186 if (pthread_rwlock_wrlock(&fIndexNameLock) == 0) {
187 delete[] fIndexName;
188 fIndexName = tmp;
189 fHttpdServer->index_name = fIndexName;
190 pthread_rwlock_unlock(&fIndexNameLock);
191 } else {
192 delete[] tmp;
193 return B_ERROR;
196 return B_OK;
200 int32 PoorManServer::_Listener(void* data)
202 PRINT(("The listener thread is working.\n"));
203 int retval;
204 thread_id tid;
205 httpd_conn* hc;
206 PoorManServer* s = static_cast<PoorManServer*>(data);
208 while (s->fIsRunning) {
209 hc = new httpd_conn;
210 hc->initialized = 0;
211 PRINT(("calling httpd_get_conn()\n"));
212 retval = //accept(), blocked here
213 httpd_get_conn(s->fHttpdServer, s->fHttpdServer->listen4_fd, hc);
214 switch (retval) {
215 case GC_OK:
216 break;
217 case GC_FAIL:
218 httpd_destroy_conn(hc);
219 delete hc;
220 s->fIsRunning = false;
221 return -1;
222 case GC_NO_MORE:
223 //should not happen, since we have a blocking socket
224 httpd_destroy_conn(hc);
225 continue;
226 break;
227 default:
228 //shouldn't happen
229 continue;
230 break;
233 if (s->fCurConns > s->fMaxConns) {
234 httpd_send_err(hc, 503,
235 httpd_err503title, (char *)"", httpd_err503form, (char *)"");
236 httpd_write_response(hc);
237 continue;
240 tid = spawn_thread(
241 PoorManServer::_Worker,
242 "www connection",
243 B_NORMAL_PRIORITY,
244 static_cast<void*>(s)
246 if (tid < B_OK) {
247 continue;
249 /*We don't check the return code here.
250 *As we can't kill a thread that doesn't receive the
251 *httpd_conn, we simply let it die itself.
253 send_data(tid, 512, &hc, sizeof(httpd_conn*));
254 atomic_add(&s->fCurConns, 1);
255 resume_thread(tid);
256 }//while
257 return 0;
261 int32 PoorManServer::_Worker(void* data)
263 static const struct timeval kTimeVal = {60, 0};
264 PoorManServer* s = static_cast<PoorManServer*>(data);
265 httpd_conn* hc;
266 int retval;
268 if (has_data(find_thread(NULL))) {
269 thread_id sender;
270 if (receive_data(&sender, &hc, sizeof(httpd_conn*)) != 512)
271 goto cleanup;
272 } else {
273 // No need to go throught the whole cleanup, as we haven't open
274 // nor allocated ht yet.
275 atomic_add(&s->fCurConns, -1);
276 return 0;
279 PRINT(("A worker thread starts to work.\n"));
281 setsockopt(hc->conn_fd, SOL_SOCKET, SO_RCVTIMEO, &kTimeVal,
282 sizeof(struct timeval));
283 retval = recv(
284 hc->conn_fd,
285 &(hc->read_buf[hc->read_idx]),
286 hc->read_size - hc->read_idx,
289 if (retval < 0)
290 goto cleanup;
292 hc->read_idx += retval;
293 switch(httpd_got_request(hc)) {
294 case GR_GOT_REQUEST:
295 break;
296 case GR_BAD_REQUEST:
297 httpd_send_err(hc, 400,
298 httpd_err400title, (char *)"", httpd_err400form, (char *)"");
299 httpd_write_response(hc);//fall through
300 case GR_NO_REQUEST: //fall through
301 default: //won't happen
302 goto cleanup;
303 break;
306 if (httpd_parse_request(hc) < 0) {
307 httpd_write_response(hc);
308 goto cleanup;
311 retval = httpd_start_request(hc, (struct timeval*)0);
312 if (retval < 0) {
313 httpd_write_response(hc);
314 goto cleanup;
317 /*true means the connection is already handled
318 *by the directory index generator in httpd_start_request().
320 if (hc->processed_directory_index == 1) {
321 if (hc->method == METHOD_GET) {
322 static_cast<PoorManApplication*>(be_app)->GetPoorManWindow()->SetHits(
323 static_cast<PoorManApplication*>(be_app)->GetPoorManWindow()->GetHits() + 1
326 goto cleanup;
329 switch (hc->method) {
330 case METHOD_GET:
331 s->_HandleGet(hc);
332 break;
333 case METHOD_HEAD:
334 s->_HandleHead(hc);
335 break;
336 case METHOD_POST:
337 s->_HandlePost(hc);
338 break;
341 cleanup: ;
342 httpd_close_conn(hc, (struct timeval*)0);
343 httpd_destroy_conn(hc);
345 delete hc;
346 atomic_add(&s->fCurConns, -1);
347 return 0;
351 status_t PoorManServer::_HandleGet(httpd_conn* hc)
353 PRINT(("HandleGet() called\n"));
355 ssize_t bytesRead;
356 uint8* buf;
357 BString log;
359 BFile file(hc->expnfilename, B_READ_ONLY);
360 if (file.InitCheck() != B_OK)
361 return B_ERROR;
363 buf = new uint8[POOR_MAN_BUF_SIZE];
364 if (buf == NULL)
365 return B_ERROR;
367 static_cast<PoorManApplication*>(be_app)->GetPoorManWindow()->SetHits(
368 static_cast<PoorManApplication*>(be_app)->
369 GetPoorManWindow()->GetHits() + 1);
371 log.SetTo("Sending file: ");
372 if (pthread_rwlock_rdlock(&fWebDirLock) == 0) {
373 log << hc->hs->cwd;
374 pthread_rwlock_unlock(&fWebDirLock);
376 log << '/' << hc->expnfilename << '\n';
377 poorman_log(log.String(), true, hc->client_addr.sa_in.sin_addr.s_addr);
379 //send mime headers
380 if (send(hc->conn_fd, hc->response, hc->responselen, 0) < 0) {
381 delete [] buf;
382 return B_ERROR;
385 file.Seek(hc->first_byte_index, SEEK_SET);
386 while (true) {
387 bytesRead = file.Read(buf, POOR_MAN_BUF_SIZE);
388 if (bytesRead == 0)
389 break;
390 else if (bytesRead < 0) {
391 delete [] buf;
392 return B_ERROR;
394 if (send(hc->conn_fd, (void*)buf, bytesRead, 0) < 0) {
395 log.SetTo("Error sending file: ");
396 if (pthread_rwlock_rdlock(&fWebDirLock) == 0) {
397 log << hc->hs->cwd;
398 pthread_rwlock_unlock(&fWebDirLock);
400 log << '/' << hc->expnfilename << '\n';
401 poorman_log(log.String(), true, hc->client_addr.sa_in.sin_addr.s_addr, RED);
402 delete [] buf;
403 return B_ERROR;
407 delete [] buf;
408 return B_OK;
412 status_t PoorManServer::_HandleHead(httpd_conn* hc)
414 int retval = send(hc->conn_fd, hc->response, hc->responselen, 0);
415 if (retval == -1)
416 return B_ERROR;
417 return B_OK;
421 status_t PoorManServer::_HandlePost(httpd_conn* hc)
423 //not implemented
424 return B_OK;
428 pthread_rwlock_t* get_web_dir_lock()
430 return static_cast<PoorManApplication*>(be_app)->
431 GetPoorManWindow()->GetServer()->GetWebDirLock();
435 pthread_rwlock_t* get_index_name_lock()
437 return static_cast<PoorManApplication*>(be_app)->
438 GetPoorManWindow()->GetServer()->GetIndexNameLock();