2 * Copyright 2009 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Ma Jie, china.majie at gmail
9 #include "PoorManServer.h"
14 #include <time.h> //for struct timeval
15 #include <sys/socket.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.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
)
36 fIndexName(new char[strlen(idxName
) + 1]),
39 fHttpdServer
= httpd_initialize(
41 (httpd_sockaddr
*)0,//sa4P
42 (httpd_sockaddr
*)0,//sa6P
43 (unsigned short)80,//port
44 (char*)0,//cgi pattern
46 (char *)"iso-8859-1",//charset
49 const_cast<char*>(webDir
),//cwd
55 (char*)0,//url_pattern
56 (char*)0,//local_pattern
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()
78 httpd_terminate(fHttpdServer
);
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
);
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)
101 fListenerTid
= spawn_thread(
102 PoorManServer::_Listener
,
105 static_cast<void*>(this)
107 if (fListenerTid
< B_OK
) {
108 poorman_log("can't create listener thread.\n", false, INADDR_NONE
, RED
);
112 if (resume_thread(fListenerTid
) != B_OK
) {
117 //our server is up and running
122 status_t
PoorManServer::Stop()
126 httpd_unlisten(fHttpdServer
);
132 /*The Web Dir is not changed if an error occured.
134 status_t
PoorManServer::SetWebDir(const char* webDir
)
136 if (chdir(webDir
) == -1) {
141 char* tmp
= strdup(webDir
);
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
);
161 status_t
PoorManServer::SetMaxConns(int32 count
)
168 status_t
PoorManServer::SetListDir(bool listDir
)
170 fHttpdServer
->do_list_dir
= (listDir
? 1 : 0);
175 status_t
PoorManServer::SetIndexName(const char* idxName
)
177 size_t length
= strlen(idxName
);
178 if (length
> B_PATH_NAME_LENGTH
+ 1)
181 char* tmp
= new char[length
+ 1];
185 strcpy(tmp
, idxName
);
186 if (pthread_rwlock_wrlock(&fIndexNameLock
) == 0) {
189 fHttpdServer
->index_name
= fIndexName
;
190 pthread_rwlock_unlock(&fIndexNameLock
);
200 int32
PoorManServer::_Listener(void* data
)
202 PRINT(("The listener thread is working.\n"));
206 PoorManServer
* s
= static_cast<PoorManServer
*>(data
);
208 while (s
->fIsRunning
) {
211 PRINT(("calling httpd_get_conn()\n"));
212 retval
= //accept(), blocked here
213 httpd_get_conn(s
->fHttpdServer
, s
->fHttpdServer
->listen4_fd
, hc
);
218 httpd_destroy_conn(hc
);
220 s
->fIsRunning
= false;
223 //should not happen, since we have a blocking socket
224 httpd_destroy_conn(hc
);
233 if (s
->fCurConns
> s
->fMaxConns
) {
234 httpd_send_err(hc
, 503,
235 httpd_err503title
, (char *)"", httpd_err503form
, (char *)"");
236 httpd_write_response(hc
);
241 PoorManServer::_Worker
,
244 static_cast<void*>(s
)
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);
261 int32
PoorManServer::_Worker(void* data
)
263 static const struct timeval kTimeVal
= {60, 0};
264 PoorManServer
* s
= static_cast<PoorManServer
*>(data
);
268 if (has_data(find_thread(NULL
))) {
270 if (receive_data(&sender
, &hc
, sizeof(httpd_conn
*)) != 512)
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);
279 PRINT(("A worker thread starts to work.\n"));
281 setsockopt(hc
->conn_fd
, SOL_SOCKET
, SO_RCVTIMEO
, &kTimeVal
,
282 sizeof(struct timeval
));
285 &(hc
->read_buf
[hc
->read_idx
]),
286 hc
->read_size
- hc
->read_idx
,
292 hc
->read_idx
+= retval
;
293 switch(httpd_got_request(hc
)) {
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
306 if (httpd_parse_request(hc
) < 0) {
307 httpd_write_response(hc
);
311 retval
= httpd_start_request(hc
, (struct timeval
*)0);
313 httpd_write_response(hc
);
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
329 switch (hc
->method
) {
342 httpd_close_conn(hc
, (struct timeval
*)0);
343 httpd_destroy_conn(hc
);
346 atomic_add(&s
->fCurConns
, -1);
351 status_t
PoorManServer::_HandleGet(httpd_conn
* hc
)
353 PRINT(("HandleGet() called\n"));
359 BFile
file(hc
->expnfilename
, B_READ_ONLY
);
360 if (file
.InitCheck() != B_OK
)
363 buf
= new uint8
[POOR_MAN_BUF_SIZE
];
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) {
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
);
380 if (send(hc
->conn_fd
, hc
->response
, hc
->responselen
, 0) < 0) {
385 file
.Seek(hc
->first_byte_index
, SEEK_SET
);
387 bytesRead
= file
.Read(buf
, POOR_MAN_BUF_SIZE
);
390 else if (bytesRead
< 0) {
394 if (send(hc
->conn_fd
, (void*)buf
, bytesRead
, 0) < 0) {
395 log
.SetTo("Error sending file: ");
396 if (pthread_rwlock_rdlock(&fWebDirLock
) == 0) {
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
);
412 status_t
PoorManServer::_HandleHead(httpd_conn
* hc
)
414 int retval
= send(hc
->conn_fd
, hc
->response
, hc
->responselen
, 0);
421 status_t
PoorManServer::_HandlePost(httpd_conn
* hc
)
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();