author | Ryan C. Gordon <icculus@icculus.org> |
Tue, 22 Jan 2008 03:44:49 +0000 | |
changeset 912 | a68ff7aaf9a3 |
parent 809 | 116b8fe30371 |
child 1016 | 957c97389257 |
child 1022 | 38a62549ad4c |
permissions | -rw-r--r-- |
206 | 1 |
/* |
2 |
* This is a quick and dirty HTTP server that uses PhysicsFS to retrieve |
|
3 |
* files. It is not robust at all, probably buggy, and definitely poorly |
|
4 |
* designed. It's just meant to show that it can be done. |
|
5 |
* |
|
6 |
* Basically, you compile this code, and run it: |
|
7 |
* ./physfshttpd archive1.zip archive2.zip /path/to/a/real/dir etc... |
|
8 |
* |
|
9 |
* The files are appended in order to the PhysicsFS search path, and when |
|
10 |
* a client request comes it, it looks for the file in said search path. |
|
11 |
* |
|
12 |
* My goal was to make this work in less than 300 lines of C, so again, it's |
|
13 |
* not to be used for any serious purpose. Patches to make this application |
|
14 |
* suck less will be readily and gratefully accepted. |
|
15 |
* |
|
16 |
* Command line I used to build this on Linux: |
|
17 |
* gcc -Wall -Werror -g -o bin/physfshttpd extras/physfshttpd.c -lphysfs |
|
18 |
* |
|
19 |
* License: this code is public domain. I make no warranty that it is useful, |
|
20 |
* correct, harmless, or environmentally safe. |
|
21 |
* |
|
22 |
* This particular file may be used however you like, including copying it |
|
23 |
* verbatim into a closed-source project, exploiting it commercially, and |
|
24 |
* removing any trace of my name from the source (although I hope you won't |
|
25 |
* do that). I welcome enhancements and corrections to this file, but I do |
|
576
5da65f8e9a50
Switched to zlib license.
Ryan C. Gordon <icculus@icculus.org>
parents:
335
diff
changeset
|
26 |
* not require you to send me patches if you make changes. This code has |
5da65f8e9a50
Switched to zlib license.
Ryan C. Gordon <icculus@icculus.org>
parents:
335
diff
changeset
|
27 |
* NO WARRANTY. |
206 | 28 |
* |
576
5da65f8e9a50
Switched to zlib license.
Ryan C. Gordon <icculus@icculus.org>
parents:
335
diff
changeset
|
29 |
* Unless otherwise stated, the rest of PhysicsFS falls under the zlib license. |
809
116b8fe30371
Renamed LICENSE to LICENSE.txt
Ryan C. Gordon <icculus@icculus.org>
parents:
767
diff
changeset
|
30 |
* Please see LICENSE.txt in the root of the source tree. |
206 | 31 |
* |
767
db29bf06d171
Changed my email address.
Ryan C. Gordon <icculus@icculus.org>
parents:
654
diff
changeset
|
32 |
* This file was written by Ryan C. Gordon. (icculus@icculus.org). |
206 | 33 |
*/ |
34 |
||
35 |
#include <stdio.h> |
|
36 |
#include <stdlib.h> |
|
37 |
#include <string.h> |
|
38 |
#include <unistd.h> |
|
39 |
#include <errno.h> |
|
40 |
#include <ctype.h> |
|
41 |
#include <sys/types.h> |
|
42 |
#include <sys/socket.h> |
|
43 |
#include <netinet/in.h> |
|
44 |
#include <arpa/inet.h> |
|
45 |
||
46 |
#ifndef LACKING_SIGNALS |
|
47 |
#include <signal.h> |
|
48 |
#endif |
|
49 |
||
50 |
#ifndef LACKING_PROTOENT |
|
51 |
#include <netdb.h> |
|
52 |
#endif |
|
53 |
||
54 |
#include "physfs.h" |
|
55 |
||
56 |
||
57 |
#define DEFAULT_PORTNUM 6667 |
|
58 |
||
59 |
typedef struct |
|
60 |
{ |
|
61 |
int sock; |
|
62 |
struct sockaddr *addr; |
|
63 |
socklen_t addrlen; |
|
64 |
} http_args; |
|
65 |
||
66 |
||
67 |
static char *txt404 = |
|
68 |
"HTTP/1.0 404 Not Found\n" |
|
69 |
"Connection: close\n" |
|
912
a68ff7aaf9a3
Fixed HTTP header in physfshttpd.c.
Ryan C. Gordon <icculus@icculus.org>
parents:
809
diff
changeset
|
70 |
"Content-Type: text/html; charset=utf-8\n" |
206 | 71 |
"\n" |
72 |
"<html><head><title>404 Not Found</title></head>\n" |
|
73 |
"<body>Can't find that.</body></html>\n\n"; |
|
74 |
||
75 |
||
76 |
static void feed_file_http(const char *ipstr, int sock, const char *fname) |
|
77 |
{ |
|
654
c0ae01de361d
Changed PHYSFS_file to PHYSFS_File to match rest of API's naming
Ryan C. Gordon <icculus@icculus.org>
parents:
576
diff
changeset
|
78 |
PHYSFS_File *in = PHYSFS_openRead(fname); |
206 | 79 |
char buffer[1024]; |
80 |
printf("%s: requested [%s].\n", ipstr, fname); |
|
81 |
if (in == NULL) |
|
82 |
{ |
|
83 |
printf("%s: Can't open [%s]: %s.\n", |
|
84 |
ipstr, fname, PHYSFS_getLastError()); |
|
335 | 85 |
write(sock, txt404, strlen(txt404)); /* !!! FIXME: Check retval */ |
206 | 86 |
} /* if */ |
87 |
else |
|
88 |
{ |
|
89 |
do |
|
90 |
{ |
|
91 |
PHYSFS_sint64 br = PHYSFS_read(in, buffer, 1, sizeof (buffer)); |
|
92 |
if (br == -1) |
|
93 |
{ |
|
94 |
printf("%s: Read error: %s.\n", ipstr, PHYSFS_getLastError()); |
|
95 |
break; |
|
96 |
} /* if */ |
|
97 |
||
335 | 98 |
write(sock, buffer, (int) br); /* !!! FIXME: CHECK THIS RETVAL! */ |
206 | 99 |
} while (!PHYSFS_eof(in)); |
100 |
||
101 |
PHYSFS_close(in); |
|
102 |
} /* else */ |
|
103 |
} /* feed_file_http */ |
|
104 |
||
105 |
||
106 |
static void *do_http(void *_args) |
|
107 |
{ |
|
108 |
http_args *args = (http_args *) _args; |
|
109 |
char ipstr[128]; |
|
110 |
char buffer[512]; |
|
111 |
char *ptr; |
|
112 |
strncpy(ipstr, inet_ntoa(((struct sockaddr_in *) args->addr)->sin_addr), |
|
113 |
sizeof (ipstr)); |
|
114 |
ipstr[sizeof (ipstr) - 1] = '\0'; |
|
115 |
||
116 |
printf("%s: connected.\n", ipstr); |
|
117 |
read(args->sock, buffer, sizeof (buffer)); |
|
118 |
buffer[sizeof (buffer) - 1] = '\0'; |
|
119 |
ptr = strchr(buffer, '\n'); |
|
120 |
if (!ptr) |
|
121 |
printf("%s: potentially bogus request.\n", ipstr); |
|
122 |
else |
|
123 |
{ |
|
124 |
*ptr = '\0'; |
|
125 |
ptr = strchr(buffer, '\r'); |
|
126 |
if (ptr != NULL) |
|
127 |
*ptr = '\0'; |
|
128 |
||
129 |
if ((toupper(buffer[0]) == 'G') && |
|
130 |
(toupper(buffer[1]) == 'E') && |
|
131 |
(toupper(buffer[2]) == 'T') && |
|
132 |
(toupper(buffer[3]) == ' ') && |
|
133 |
(toupper(buffer[4]) == '/')) |
|
134 |
{ |
|
135 |
ptr = strchr(buffer + 5, ' '); |
|
136 |
if (ptr != NULL) |
|
137 |
*ptr = '\0'; |
|
138 |
feed_file_http(ipstr, args->sock, buffer + 4); |
|
139 |
} /* if */ |
|
140 |
} /* else */ |
|
141 |
||
142 |
/* !!! FIXME: Time the transfer. */ |
|
143 |
printf("%s: closing connection.\n", ipstr); |
|
144 |
close(args->sock); |
|
145 |
free(args->addr); |
|
146 |
free(args); |
|
147 |
return(NULL); |
|
148 |
} /* do_http */ |
|
149 |
||
150 |
||
151 |
static void serve_http_request(int sock, struct sockaddr *addr, |
|
152 |
socklen_t addrlen) |
|
153 |
{ |
|
154 |
http_args *args = (http_args *) malloc(sizeof (http_args)); |
|
155 |
if (args == NULL) |
|
156 |
{ |
|
157 |
printf("out of memory.\n"); |
|
158 |
return; |
|
159 |
} // if |
|
160 |
args->addr = (struct sockaddr *) malloc(addrlen); |
|
161 |
if (args->addr == NULL) |
|
162 |
{ |
|
163 |
free(args); |
|
164 |
printf("out of memory.\n"); |
|
165 |
return; |
|
166 |
} // if |
|
167 |
||
168 |
args->sock = sock; |
|
169 |
args->addrlen = addrlen; |
|
170 |
memcpy(args->addr, addr, addrlen); |
|
171 |
||
172 |
/* !!! FIXME: optionally spin a thread... */ |
|
173 |
do_http((void *) args); |
|
174 |
} /* server_http_request */ |
|
175 |
||
176 |
||
177 |
static int create_listen_socket(short portnum) |
|
178 |
{ |
|
179 |
int retval = -1; |
|
180 |
int protocol = 0; /* pray this is right. */ |
|
181 |
||
182 |
#ifndef LACKING_PROTOENT |
|
183 |
struct protoent *prot; |
|
184 |
setprotoent(0); |
|
185 |
prot = getprotobyname("tcp"); |
|
186 |
if (prot != NULL) |
|
187 |
protocol = prot->p_proto; |
|
188 |
#endif |
|
189 |
||
190 |
retval = socket(PF_INET, SOCK_STREAM, protocol); |
|
191 |
if (retval >= 0) |
|
192 |
{ |
|
193 |
struct sockaddr_in addr; |
|
194 |
addr.sin_family = AF_INET; |
|
195 |
addr.sin_port = htons(portnum); |
|
196 |
addr.sin_addr.s_addr = INADDR_ANY; |
|
197 |
if ((bind(retval, &addr, (socklen_t) sizeof (addr)) == -1) || |
|
198 |
(listen(retval, 5) == -1)) |
|
199 |
{ |
|
200 |
close(retval); |
|
201 |
retval = -1; |
|
202 |
} /* if */ |
|
203 |
} /* if */ |
|
204 |
||
205 |
return(retval); |
|
206 |
} /* create_listen_socket */ |
|
207 |
||
208 |
||
209 |
static int listensocket = -1; |
|
210 |
||
211 |
void at_exit_cleanup(void) |
|
212 |
{ |
|
213 |
/* |
|
214 |
* !!! FIXME: If thread support, signal threads to terminate and |
|
215 |
* !!! FIXME: wait for them to clean up. |
|
216 |
*/ |
|
217 |
||
218 |
if (listensocket >= 0) |
|
219 |
close(listensocket); |
|
220 |
||
221 |
if (!PHYSFS_deinit()) |
|
222 |
printf("PHYSFS_deinit() failed: %s\n", PHYSFS_getLastError()); |
|
223 |
} /* at_exit_cleanup */ |
|
224 |
||
225 |
||
226 |
int main(int argc, char **argv) |
|
227 |
{ |
|
228 |
int i; |
|
229 |
int portnum = DEFAULT_PORTNUM; |
|
230 |
||
231 |
setbuf(stdout, NULL); |
|
232 |
setbuf(stderr, NULL); |
|
233 |
||
234 |
#ifndef LACKING_SIGNALS |
|
235 |
/* I'm not sure if this qualifies as a cheap trick... */ |
|
236 |
signal(SIGTERM, exit); |
|
237 |
signal(SIGINT, exit); |
|
238 |
signal(SIGFPE, exit); |
|
239 |
signal(SIGSEGV, exit); |
|
240 |
signal(SIGPIPE, exit); |
|
241 |
signal(SIGILL, exit); |
|
242 |
#endif |
|
243 |
||
244 |
if (argc == 1) |
|
245 |
{ |
|
246 |
printf("USAGE: %s <archive1> [archive2 [... archiveN]]\n", argv[0]); |
|
247 |
return(42); |
|
248 |
} /* if */ |
|
249 |
||
250 |
if (!PHYSFS_init(argv[0])) |
|
251 |
{ |
|
252 |
printf("PHYSFS_init() failed: %s\n", PHYSFS_getLastError()); |
|
253 |
return(42); |
|
254 |
} /* if */ |
|
255 |
||
256 |
/* normally, this is bad practice, but oh well. */ |
|
257 |
atexit(at_exit_cleanup); |
|
258 |
||
259 |
for (i = 1; i < argc; i++) |
|
260 |
{ |
|
261 |
if (!PHYSFS_addToSearchPath(argv[i], 1)) |
|
262 |
printf(" WARNING: failed to add [%s] to search path.\n", argv[i]); |
|
263 |
} /* else */ |
|
264 |
||
265 |
listensocket = create_listen_socket(portnum); |
|
266 |
if (listensocket < 0) |
|
267 |
{ |
|
268 |
printf("listen socket failed to create.\n"); |
|
269 |
return(42); |
|
270 |
} /* if */ |
|
271 |
||
272 |
while (1) /* infinite loop for now. */ |
|
273 |
{ |
|
274 |
struct sockaddr addr; |
|
275 |
socklen_t len; |
|
276 |
int s = accept(listensocket, &addr, &len); |
|
277 |
if (s < 0) |
|
278 |
{ |
|
279 |
printf("accept() failed: %s\n", strerror(errno)); |
|
280 |
close(listensocket); |
|
281 |
return(42); |
|
282 |
} /* if */ |
|
283 |
||
284 |
serve_http_request(s, &addr, len); |
|
285 |
} /* while */ |
|
286 |
||
287 |
return(0); |
|
288 |
} /* main */ |
|
289 |
||
290 |
/* end of physfshttpd.c ... */ |
|
291 |