Skip to content

Commit 5fa6c99

Browse files
committed
Improve web server
Improve threading Add socket poll() Add HEAD support
1 parent 02d9ced commit 5fa6c99

File tree

4 files changed

+133
-76
lines changed

4 files changed

+133
-76
lines changed

example/source/main.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ int main(s32 argc, const char* argv[])
9393
if (paddata.BTN_TRIANGLE) {
9494
// save a PNG screenshot without alpha channel in '/dev_hdd0/tmp/screenshot_YYYY_MM_DD_HH_MM_SS.png'
9595
dbglogger_log("Saving a screenshot to /dev_hdd0/tmp/ ...");
96-
// dbglogger_screenshot_tmp(0);
96+
dbglogger_screenshot_tmp(0);
9797
}
9898

9999
if(paddata.BTN_START){

include/dbglogger.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ typedef enum {
4646
} LOGGER_MODES;
4747

4848
typedef struct {
49-
char method[8];
49+
char method;
5050
char resource[256];
5151
} dWebRequest_t;
5252

source/systhread.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ inline void* sys_thread_alloc(int num)
115115

116116
inline int sys_thread_create(void* ptr_thread, void (*func)(void*), void* arg)
117117
{
118-
return sysThreadCreate((sys_ppu_thread_t*) ptr_thread, func, arg, 1002, 0x2000, THREAD_JOINABLE, "");
118+
return sysThreadCreate((sys_ppu_thread_t*) ptr_thread, func, arg, 1002, 0x8000, THREAD_JOINABLE, "");
119119
}
120120

121121
inline int sys_thread_create2(void* ptr_threads, int index, void (*func)(void*), void* arg)

source/webserver.c

+130-73
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
#include <sys/socket.h>
77
#include <netinet/in.h>
88
#include <arpa/inet.h>
9-
#include <ctype.h>
9+
#ifdef __PPU__
10+
#include <net/poll.h>
11+
#else
12+
#include <poll.h>
13+
#endif
1014

1115
#include "dbglogger.h"
1216
#include "systhread.h"
@@ -20,7 +24,7 @@ const char ERROR_PAGE[] = "<HTML><HEAD><TITLE>.:: HTTP/Error</TITLE></HEAD>"
2024
"<BODY BGCOLOR=\"#FFFFFF\" ALINK=\"#000000\" VLINK=\"#000000\" LINK=\"#000000\">"
2125
"<FONT FACE=\"Arial\"><H1>.:: <I>HTTP/Error</I></H1></FONT>"
2226
"<CENTER><IMG SRC=\"https://bucanero.github.io/bucanero/error.gif\" WIDTH=\"228\" HEIGHT=\"155\" BORDER=\"0\" ALT=\"Error\"></CENTER>"
23-
"<P ALIGN=\"RIGHT\"><B><FONT FACE=\"Arial\" SIZE=\"3\">&lt;&lt; <A HREF=\"javascript:history.go(-1);\">go back</A> ::.</FONT></B></P></BODY></HTML>";
27+
"<P ALIGN=\"RIGHT\"><B><FONT FACE=\"Arial\" SIZE=\"3\">&lt;&lt; <A HREF=\"javascript:history.go(-1);\">go back</A> ::.</FONT></B></P></BODY></HTML>\r\n";
2428

2529
typedef struct {
2630
int idx;
@@ -37,7 +41,7 @@ static struct {
3741
{".html", "text/html"},
3842
{".txt", "text/plain"},
3943
{".png", "image/png"},
40-
{".PNG", "image/png"},
44+
{".gif", "image/gif"},
4145
{".jpg", "image/jpeg"},
4246
{".xml", "application/xml"},
4347
{".css", "text/css"},
@@ -47,6 +51,8 @@ static struct {
4751

4852
static void* threads = NULL;
4953
static int run_server = 0;
54+
static int sockfd;
55+
5056

5157
static char* getContentType(const char *path)
5258
{
@@ -60,7 +66,23 @@ static char* getContentType(const char *path)
6066
return extensions[0].filetype;
6167
}
6268

63-
static int serveFile(int socket, const char* path)
69+
static void sendResponse(int fd, char* buf, const char* code, const char* type, long len, const char* page)
70+
{
71+
snprintf(buf, BUFSIZ, "HTTP/1.0 %s\r\n"
72+
"Connection: close\r\n"
73+
"Content-Type: %s\r\n"
74+
"Content-Length: %ld\r\n"
75+
"Server: dbglogger/1.0 (PlayStation)\r\n\r\n%s", code, type, len, page ? page : "");
76+
if (send(fd, buf, strlen(buf), 0) == -1)
77+
dbglogger_log("(error) send");
78+
}
79+
80+
static inline void errorPage(int fd, char* buf)
81+
{
82+
sendResponse(fd, buf, "404 Not Found", "text/html", strlen(ERROR_PAGE), ERROR_PAGE);
83+
}
84+
85+
static int serveFile(int socket, const char* path, char method)
6486
{
6587
FILE *fd;
6688
int readRet = strlen(path);
@@ -70,10 +92,7 @@ static int serveFile(int socket, const char* path)
7092

7193
if ((readRet > 0 && path[readRet-1] == '/') || (fd = fopen(path, "rb")) == NULL)
7294
{
73-
snprintf(buf, BUFSIZ, "HTTP/1.0 404 Not Found\r\nContent-Type: text/html\r\n\r\n%s\r\n", ERROR_PAGE);
74-
if (send(socket, buf, strlen(buf), 0) == -1)
75-
dbglogger_log("(error) send");
76-
95+
errorPage(socket, buf);
7796
return 404;
7897
}
7998

@@ -82,9 +101,13 @@ static int serveFile(int socket, const char* path)
82101
fseek(fd, 0, SEEK_SET);
83102

84103
// Write header
85-
snprintf(buf, BUFSIZ, "HTTP/1.0 200 OK\r\nContent-Length: %ld\r\n"
86-
"Content-Type: %s\r\nServer: dbglogger\r\n\r\n", fsize, getContentType(path));
87-
send(socket, buf, strlen(buf), 0);
104+
sendResponse(socket, buf, "200 OK", getContentType(path), fsize, NULL);
105+
106+
// skip data for HEAD method
107+
if (method == 'H') {
108+
fclose(fd);
109+
return 200;
110+
}
88111

89112
// Read file and Write to body
90113
while((readRet = fread(buf, 1, BUFSIZ, fd)) > 0)
@@ -101,13 +124,12 @@ static int serveFile(int socket, const char* path)
101124
return 200;
102125
}
103126

104-
static dWebRequest_t * parseRequest(const char *buf)
127+
static dWebRequest_t * parseRequest(const char *start)
105128
{
106129
dWebRequest_t *newRequest;
107-
const char *start = buf;
108130
const char *end;
109131

110-
if(strncmp("GET", start, 3) != 0)
132+
if(strncmp("GET", start, 3) != 0 && strncmp("HEAD", start, 4) != 0)
111133
{
112134
dbglogger_log("[!] Unsupported HTTP Method");
113135
return NULL;
@@ -116,16 +138,12 @@ static dWebRequest_t * parseRequest(const char *buf)
116138
newRequest = malloc(sizeof(dWebRequest_t));
117139
memset(newRequest, 0, sizeof(dWebRequest_t));
118140

119-
strncpy(newRequest->method, "GET", sizeof(newRequest->method));
141+
newRequest->method = start[0];
120142
// Jump past: "GET "
121-
start += 4;
122-
123-
end=start;
124-
while(*end && !isspace(*end))
125-
++end;
143+
start = strchr(start, ' ') + 1;
144+
end = strchr(start, ' ');
126145

127-
size_t pathLen = (end - start);
128-
strncpy(newRequest->resource, start, pathLen);
146+
strncpy(newRequest->resource, start, end - start);
129147
// newRequest->resource[pathLen] = '\0';
130148

131149
return newRequest;
@@ -139,55 +157,104 @@ static void* get_in_addr(struct sockaddr* sa)
139157
return NULL;
140158
}
141159

142-
static void thread_handler(void* td)
160+
static void client_handler(void* td)
143161
{
144-
int new_fd;
145162
threadData_t* data = (threadData_t*) td;
146-
struct sockaddr_in their_addr; // connector's address
147-
socklen_t sin_size = sizeof(their_addr);
148163
char buf[BUFSIZ];
149164

165+
dbglogger_printf("Thread #%d started (0x%08X)\n", data->idx, data->sockfd);
166+
150167
while (run_server)
151168
{
152-
dbglogger_printf("Thread #%d running (0x%08X)\n", data->idx, data->sockfd);
153-
154-
new_fd = accept(data->sockfd, (struct sockaddr*) &their_addr, &sin_size);
155-
if (new_fd == -1 || !run_server)
169+
if (data->sockfd < 0)
156170
{
157-
dbglogger_log("(error) accept");
158-
free(td);
159-
sys_thread_exit(0);
171+
usleep(5000);
172+
continue;
160173
}
161174

162-
inet_ntop(their_addr.sin_family, get_in_addr((struct sockaddr*) &their_addr), buf, sizeof(buf));
163-
dbglogger_printf("Thread #%d: got connection from %s\n", data->idx, buf);
175+
dbglogger_printf("Client #%d running (0x%08X)\n", data->idx, data->sockfd);
164176

165177
memset(buf, 0, BUFSIZ);
166-
recv(new_fd, buf, BUFSIZ, 0);
178+
recv(data->sockfd, buf, BUFSIZ, 0);
167179

168-
dbglogger_printf("---\n%s\n---\n", buf);
180+
dbglogger_printf("%d>>\n---\n%s\n---\n", data->idx, buf);
169181
dWebRequest_t *newRequest = parseRequest(buf);
170182

171183
// If parsing failed shutdown and exit
172184
if(newRequest)
173185
{
174186
if(data->reqHandler && data->reqHandler(newRequest, buf))
175-
serveFile(new_fd, buf);
187+
serveFile(data->sockfd, buf, newRequest->method);
188+
else
189+
errorPage(data->sockfd, buf);
176190

177191
free(newRequest);
178-
}
192+
}
193+
else sendResponse(data->sockfd, buf, "501 Not Implemented", "text/plain", 0, NULL);
194+
195+
shutdown(data->sockfd, SHUT_RDWR);
196+
close(data->sockfd);
197+
data->sockfd = -1;
198+
}
199+
200+
dbglogger_log("(end) #%d", data->idx);
201+
sys_thread_exit(0);
202+
}
203+
204+
static void httpd(void *td)
205+
{
206+
int new_fd;
207+
threadData_t* data = (threadData_t*) td;
208+
struct pollfd pfds[1];
209+
struct sockaddr_in their_addr; // connector's address
210+
socklen_t sin_size = sizeof(their_addr);
211+
char str[INET_ADDRSTRLEN];
212+
213+
pfds[0].fd = data->sockfd;
214+
pfds[0].events = POLLIN;
215+
pfds[0].revents = 0;
216+
217+
while (run_server)
218+
{
219+
dbglogger_log("webserver:%d running (0x%08X)", run_server, data->sockfd);
179220

180-
close(new_fd);
221+
new_fd = poll(pfds, 1, 500);
222+
if (new_fd <= 0 || !(pfds[0].revents & POLLIN) || !run_server)
223+
continue;
224+
225+
new_fd = accept(data->sockfd, (struct sockaddr*) &their_addr, &sin_size);
226+
if (new_fd < 0)
227+
{
228+
dbglogger_log("(error) accept");
229+
continue;
230+
}
231+
232+
inet_ntop(their_addr.sin_family, get_in_addr((struct sockaddr*) &their_addr), str, sizeof(str));
233+
dbglogger_printf("httpd #%d: got connection from %s\n", data->idx, str);
234+
235+
while (new_fd)
236+
{
237+
for (int i=1; new_fd && i <= NUM_THREADS; i++)
238+
if (data[i].sockfd < 0)
239+
{
240+
data[i].sockfd = new_fd;
241+
new_fd = 0;
242+
}
243+
usleep(new_fd ? 1000 : 0);
244+
}
181245
}
182246

183-
dbglogger_log("(end)");
247+
shutdown(data->sockfd, SHUT_RDWR);
248+
close(data->sockfd);
249+
250+
dbglogger_log("(stop) httpd");
184251
free(td);
185252
sys_thread_exit(0);
186253
}
187254

188255
int web_start(int port, dWebReqHandler_t handler)
189256
{
190-
int sockfd, yes = 1;
257+
int yes = 1;
191258
struct sockaddr_in sa;
192259

193260
memset(&sa, 0, sizeof(sa));
@@ -213,52 +280,42 @@ int web_start(int port, dWebReqHandler_t handler)
213280
return 0;
214281
}
215282

216-
if ((threads = sys_thread_alloc(NUM_THREADS)) == NULL) {
283+
if ((threads = sys_thread_alloc(NUM_THREADS+1)) == NULL) {
217284
dbglogger_log("(error) thread alloc");
218285
return 0;
219286
}
220287

221288
run_server = port;
222-
dbglogger_log("webserver:%d starting threads...", port);
289+
dbglogger_log("webserver:%d starting httpd...", port);
290+
291+
threadData_t *tdata = malloc(sizeof(threadData_t) * (NUM_THREADS+1));
292+
tdata[0].idx = 0;
293+
tdata[0].sockfd = sockfd;
294+
tdata[0].reqHandler = NULL;
223295

224-
for (int i = 0; i < NUM_THREADS; ++i)
296+
sys_thread_create2(threads, 0, &httpd, tdata);
297+
298+
for (int i = 1; i <= NUM_THREADS; i++)
225299
{
226-
threadData_t *tdata = malloc(sizeof(threadData_t));
227-
tdata->idx = i;
228-
tdata->sockfd = sockfd;
229-
tdata->reqHandler = handler;
300+
tdata[i].idx = i;
301+
tdata[i].sockfd = -1;
302+
tdata[i].reqHandler = handler;
230303

231-
sys_thread_create2(threads, i, &thread_handler, tdata);
304+
sys_thread_create2(threads, i, &client_handler, &tdata[i]);
232305
}
233306

234-
dbglogger_log("webserver:%d running", port);
235307
return 1;
236308
}
237309

238-
static void end_socket(int port)
239-
{
240-
struct sockaddr_in stSockAddr;
241-
int socketFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
242-
243-
memset(&stSockAddr, 0, sizeof(stSockAddr));
244-
stSockAddr.sin_family = AF_INET;
245-
stSockAddr.sin_port = htons(port);
246-
inet_pton(AF_INET, "127.0.0.1", &stSockAddr.sin_addr);
247-
248-
connect(socketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr));
249-
close(socketFD);
250-
}
251-
252310
void web_stop()
253311
{
254-
int port = run_server;
255-
run_server = 0;
256-
257-
for (int i = 0; i < NUM_THREADS; ++i)
258-
{
259-
dbglogger_log("stop %d", i);
260-
end_socket(port);
261-
}
312+
if (!run_server)
313+
return;
262314

315+
run_server = 0;
316+
shutdown(sockfd, SHUT_RDWR);
317+
close(sockfd);
263318
sys_thread_free(threads);
319+
320+
dbglogger_log("webserver:off");
264321
}

0 commit comments

Comments
 (0)