| 1 | |
|---|
| 2 | #include "mini_httpd.h" |
|---|
| 3 | #include "mini_httpd_defs.h" |
|---|
| 4 | #include "bos.h" |
|---|
| 5 | #include "bos_task_priorities.h" |
|---|
| 6 | |
|---|
| 7 | #define MH_TASK_STACK_WORDS 512 |
|---|
| 8 | |
|---|
| 9 | typedef struct mh_server_t |
|---|
| 10 | { |
|---|
| 11 | mh_post_cb_t post_cb; |
|---|
| 12 | struct sockaddr_in cli_addr; |
|---|
| 13 | struct sockaddr_in serv_addr; |
|---|
| 14 | int port; |
|---|
| 15 | int listenfd; |
|---|
| 16 | int socketfd; |
|---|
| 17 | unsigned int task_stack[MH_TASK_STACK_WORDS]; |
|---|
| 18 | b_task_t task_h; |
|---|
| 19 | char buffer[MH_BUFSIZE+1]; |
|---|
| 20 | char path[MH_MAX_PATH_SIZE+1]; |
|---|
| 21 | char fullpath[MH_MAX_FULLPATH_SIZE+1]; |
|---|
| 22 | bool done; |
|---|
| 23 | }mh_server_t; |
|---|
| 24 | |
|---|
| 25 | char MH_ERROR_PAGE[] = "<html><head><title>\nServer Error</title></head>\n<body bgcolor=red text=black>\nERROR: %s %s\n</body></html>\n"; |
|---|
| 26 | |
|---|
| 27 | typedef struct file_extensions_t |
|---|
| 28 | { |
|---|
| 29 | char *ext; |
|---|
| 30 | char *filetype; |
|---|
| 31 | } file_extensions_t; |
|---|
| 32 | |
|---|
| 33 | file_extensions_t mime_type_map[] = |
|---|
| 34 | { |
|---|
| 35 | {"gif", "image/gif" }, |
|---|
| 36 | {"jpg", "image/jpeg"}, |
|---|
| 37 | {"jpeg","image/jpeg"}, |
|---|
| 38 | {"png", "image/png" }, |
|---|
| 39 | {"zip", "image/zip" }, |
|---|
| 40 | {"gz", "image/gz" }, |
|---|
| 41 | {"tar", "image/tar" }, |
|---|
| 42 | {"htm", "text/html" }, |
|---|
| 43 | {"html","text/html" }, |
|---|
| 44 | {0,0} |
|---|
| 45 | }; |
|---|
| 46 | |
|---|
| 47 | |
|---|
| 48 | static int mh_startup(mh_server_t *p_server) |
|---|
| 49 | { |
|---|
| 50 | #ifdef WIN32 |
|---|
| 51 | WSADATA wsaData; |
|---|
| 52 | int iResult; |
|---|
| 53 | |
|---|
| 54 | iResult = WSAStartup(MAKEWORD(2,2), &wsaData); |
|---|
| 55 | if (iResult != 0) { |
|---|
| 56 | printf("WSAStartup failed with error: %d\n", iResult); |
|---|
| 57 | return 1; |
|---|
| 58 | } |
|---|
| 59 | #endif |
|---|
| 60 | return 0; |
|---|
| 61 | } |
|---|
| 62 | static void mh_shutdown(mh_server_t *p_server) |
|---|
| 63 | { |
|---|
| 64 | #ifdef WIN32 |
|---|
| 65 | WSACleanup(); |
|---|
| 66 | #endif |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | static void *mh_realloc(void* ptr,int new_size) |
|---|
| 70 | { |
|---|
| 71 | void * nptr = mh_malloc(new_size); |
|---|
| 72 | if (!nptr) |
|---|
| 73 | return NULL; |
|---|
| 74 | |
|---|
| 75 | mh_memcpy(nptr,ptr,new_size); |
|---|
| 76 | mh_free(ptr); |
|---|
| 77 | return nptr; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | static int mh_open(char *fname) |
|---|
| 81 | { |
|---|
| 82 | FILE *f = fopen(fname,"rb"); |
|---|
| 83 | if (f == NULL) |
|---|
| 84 | return -1; |
|---|
| 85 | return (int)f; |
|---|
| 86 | } |
|---|
| 87 | static void mh_close(int fd) |
|---|
| 88 | { |
|---|
| 89 | fclose((FILE*)fd); |
|---|
| 90 | } |
|---|
| 91 | static int mh_read(int fd, void* buf, int len) |
|---|
| 92 | { |
|---|
| 93 | return (int)fread(buf,1,len,(FILE*)fd); |
|---|
| 94 | } |
|---|
| 95 | |
|---|
| 96 | static char* mh_get_token (char *str, int *idx) |
|---|
| 97 | { |
|---|
| 98 | char *token = (char*)mh_malloc (MH_MAX_TOKEN_SIZE); |
|---|
| 99 | int i = 0; |
|---|
| 100 | int tsize = MH_MAX_TOKEN_SIZE; |
|---|
| 101 | char c = 'a'; |
|---|
| 102 | |
|---|
| 103 | while ( (str[i] != ' ') && (str[i] != 0) && (str[i] != 13)) |
|---|
| 104 | { |
|---|
| 105 | c = str[i]; |
|---|
| 106 | token[i++] = c; |
|---|
| 107 | if (i >= tsize) |
|---|
| 108 | { |
|---|
| 109 | tsize += 256; |
|---|
| 110 | token = (char*)mh_realloc ((void*)token,tsize); |
|---|
| 111 | } |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | if (i == 0) |
|---|
| 115 | { |
|---|
| 116 | mh_free(token); |
|---|
| 117 | return NULL; |
|---|
| 118 | |
|---|
| 119 | } |
|---|
| 120 | |
|---|
| 121 | token[i] = 0; |
|---|
| 122 | *idx = i + 1; |
|---|
| 123 | MH_DBG_LOG(eMH_DBG_WRN,("Token: %s",token)); |
|---|
| 124 | |
|---|
| 125 | return token; |
|---|
| 126 | } |
|---|
| 127 | |
|---|
| 128 | static char* mh_get_mime_type(char * str) |
|---|
| 129 | { |
|---|
| 130 | char *fstr = NULL; |
|---|
| 131 | int slen,len,i; |
|---|
| 132 | /* work out the file type and check we support it */ |
|---|
| 133 | slen = mh_strlen(str); |
|---|
| 134 | |
|---|
| 135 | for ( i = 0; mime_type_map[i].ext != 0;i++) |
|---|
| 136 | { |
|---|
| 137 | len = mh_strlen(mime_type_map[i].ext); |
|---|
| 138 | if (!mh_strncmp(&str[slen-len], mime_type_map[i].ext, len)) |
|---|
| 139 | { |
|---|
| 140 | fstr = mime_type_map[i].filetype; |
|---|
| 141 | break; |
|---|
| 142 | } |
|---|
| 143 | } |
|---|
| 144 | return fstr; |
|---|
| 145 | } |
|---|
| 146 | static mh_cmd_t mh_cmd(char *cmd) |
|---|
| 147 | { |
|---|
| 148 | if (cmd) |
|---|
| 149 | { |
|---|
| 150 | if (mh_strcmp (cmd,"GET") == 0) { |
|---|
| 151 | return eMH_CMD_GET; |
|---|
| 152 | } |
|---|
| 153 | if (mh_strcmp (cmd,"POST") == 0) { |
|---|
| 154 | return eMH_CMD_POST; |
|---|
| 155 | } |
|---|
| 156 | if (mh_strcmp (cmd,"HEAD") == 0) { |
|---|
| 157 | return eMH_CMD_HEAD; |
|---|
| 158 | } |
|---|
| 159 | } |
|---|
| 160 | return eMH_CMD_NONE; |
|---|
| 161 | } |
|---|
| 162 | |
|---|
| 163 | static void mh_send_error (mh_server_t *p_server, char *str, mh_send_err_t err) |
|---|
| 164 | { |
|---|
| 165 | int len; |
|---|
| 166 | switch (err) |
|---|
| 167 | { |
|---|
| 168 | case eMH_SEND_ERR_NO_FILE: |
|---|
| 169 | len = mh_sprintf(p_server->buffer,MH_ERROR_PAGE,str,"NO FILE"); |
|---|
| 170 | mh_send(p_server->socketfd,p_server->buffer,len); |
|---|
| 171 | break; |
|---|
| 172 | case eMH_SEND_ERR_UNSUPPORTED: |
|---|
| 173 | default: |
|---|
| 174 | len = mh_sprintf(p_server->buffer,MH_ERROR_PAGE,str,"UNSUPPORTED"); |
|---|
| 175 | mh_send(p_server->socketfd,p_server->buffer,len); |
|---|
| 176 | break; |
|---|
| 177 | } |
|---|
| 178 | } |
|---|
| 179 | static int mh_send_file(mh_server_t *p_server, char* filepath) |
|---|
| 180 | { |
|---|
| 181 | int file_fd = -1; |
|---|
| 182 | int ret; |
|---|
| 183 | char *mime_str = mh_get_mime_type(filepath); |
|---|
| 184 | |
|---|
| 185 | if (!mime_str) |
|---|
| 186 | { |
|---|
| 187 | MH_DBG_LOG(eMH_DBG_ERR,("File type not supported %s",filepath)); |
|---|
| 188 | return -1; |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | mh_sprintf(p_server->fullpath,"%s%s",p_server->path,filepath); |
|---|
| 192 | |
|---|
| 193 | if (( file_fd = mh_open(p_server->fullpath)) == -1) /* open the file for reading */ |
|---|
| 194 | { |
|---|
| 195 | MH_DBG_LOG(eMH_DBG_ERR,("failed to open file %s",p_server->fullpath)); |
|---|
| 196 | return -1; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | MH_DBG_LOG(eMH_DBG_MSG,("SEND %s",p_server->fullpath)); |
|---|
| 200 | |
|---|
| 201 | mh_sprintf(p_server->buffer,"HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", mime_str); |
|---|
| 202 | mh_send(p_server->socketfd,p_server->buffer,mh_strlen(p_server->buffer)); |
|---|
| 203 | |
|---|
| 204 | /* send file in 8KB block - last block may be smaller */ |
|---|
| 205 | while ( (ret = mh_read(file_fd, p_server->buffer, MH_BUFSIZE)) > 0 ) { |
|---|
| 206 | mh_send(p_server->socketfd,p_server->buffer,ret); |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | mh_close(file_fd); |
|---|
| 210 | return 0; |
|---|
| 211 | } |
|---|
| 212 | |
|---|
| 213 | static int mh_handle_request(mh_server_t *p_server) |
|---|
| 214 | { |
|---|
| 215 | int idx, ret,free_path; |
|---|
| 216 | char *token = NULL; |
|---|
| 217 | char *path = NULL; |
|---|
| 218 | |
|---|
| 219 | free_path = 1; |
|---|
| 220 | ret = mh_recv(p_server->socketfd,p_server->buffer,MH_BUFSIZE); /* read Web request in one go */ |
|---|
| 221 | if ((ret == 0) || (ret == -1)) /* read failure stop now */ |
|---|
| 222 | { |
|---|
| 223 | MH_DBG_LOG(eMH_DBG_ERR,("failed to read browser request %d",mh_geterror(0))); |
|---|
| 224 | goto done; |
|---|
| 225 | } |
|---|
| 226 | p_server->buffer[ret]=0; /* terminate the buffer */ |
|---|
| 227 | |
|---|
| 228 | idx = 0; |
|---|
| 229 | token = mh_get_token (p_server->buffer, &idx); |
|---|
| 230 | |
|---|
| 231 | switch (mh_cmd (token)) |
|---|
| 232 | { |
|---|
| 233 | case eMH_CMD_GET: |
|---|
| 234 | path = mh_get_token (&p_server->buffer[idx], &idx); |
|---|
| 235 | if ((path == NULL) || ((path[0] == '/') && (path[1] == '\0'))) |
|---|
| 236 | { |
|---|
| 237 | path = "/index.html"; |
|---|
| 238 | free_path = 0; |
|---|
| 239 | } |
|---|
| 240 | if (mh_send_file(p_server,path) != 0) |
|---|
| 241 | mh_send_error(p_server, path, eMH_SEND_ERR_NO_FILE); |
|---|
| 242 | break; |
|---|
| 243 | case eMH_CMD_POST: |
|---|
| 244 | path = mh_get_token (&p_server->buffer[idx], &idx); |
|---|
| 245 | MH_DBG_LOG(eMH_DBG_ERR,("%s",path)); |
|---|
| 246 | if (p_server->post_cb) |
|---|
| 247 | { |
|---|
| 248 | p_server->post_cb(path); |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | path = "/index.html"; |
|---|
| 252 | free_path = 0; |
|---|
| 253 | if (mh_send_file(p_server,path) != 0) |
|---|
| 254 | mh_send_error(p_server, path, eMH_SEND_ERR_NO_FILE); |
|---|
| 255 | break; |
|---|
| 256 | case eMH_CMD_HEAD: |
|---|
| 257 | default: |
|---|
| 258 | MH_DBG_LOG(eMH_DBG_ERR,("%s: not supported",token)); |
|---|
| 259 | mh_send_error(p_server, token, eMH_SEND_ERR_UNSUPPORTED); |
|---|
| 260 | break; |
|---|
| 261 | } |
|---|
| 262 | |
|---|
| 263 | done: |
|---|
| 264 | if (token) |
|---|
| 265 | mh_free(token); |
|---|
| 266 | if (path && free_path) |
|---|
| 267 | mh_free(path); |
|---|
| 268 | |
|---|
| 269 | if (p_server->socketfd != -1) |
|---|
| 270 | mh_closesocket(p_server->socketfd); |
|---|
| 271 | p_server->socketfd = -1; |
|---|
| 272 | |
|---|
| 273 | return 0; |
|---|
| 274 | } |
|---|
| 275 | |
|---|
| 276 | |
|---|
| 277 | |
|---|
| 278 | static void mh_done(mh_server_t *p_server) |
|---|
| 279 | { |
|---|
| 280 | MH_DBG_LOG(eMH_DBG_ERR,("%s:%d\n",__FUNCTION__,__LINE__)); |
|---|
| 281 | if (p_server->listenfd != -1) |
|---|
| 282 | mh_closesocket(p_server->listenfd); |
|---|
| 283 | if (p_server->socketfd != -1) |
|---|
| 284 | mh_closesocket(p_server->socketfd); |
|---|
| 285 | |
|---|
| 286 | mh_shutdown(p_server); |
|---|
| 287 | mh_free(p_server); |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | |
|---|
| 291 | static void mh_task(void *arg) |
|---|
| 292 | { |
|---|
| 293 | mh_server_t *p_server = (mh_server_t*)arg; |
|---|
| 294 | size_t length; |
|---|
| 295 | MH_DBG_LOG(eMH_DBG_ERR,("%s:%d\n",__FUNCTION__,__LINE__)); |
|---|
| 296 | |
|---|
| 297 | while (!p_server->done) |
|---|
| 298 | { |
|---|
| 299 | if ( listen(p_server->listenfd,64) < 0) |
|---|
| 300 | { |
|---|
| 301 | MH_DBG_LOG(eMH_DBG_ERR,("listen failed %d",mh_geterror(0))); |
|---|
| 302 | bos_sleep(100); |
|---|
| 303 | continue; |
|---|
| 304 | } |
|---|
| 305 | |
|---|
| 306 | while (1) |
|---|
| 307 | { |
|---|
| 308 | length = sizeof(p_server->cli_addr); |
|---|
| 309 | if ((p_server->socketfd = accept(p_server->listenfd, (struct sockaddr *)&(p_server->cli_addr), (socklen_t*)&length)) < 0) |
|---|
| 310 | { |
|---|
| 311 | MH_DBG_LOG(eMH_DBG_ERR,("accept failed %d",mh_geterror(0))); |
|---|
| 312 | break; |
|---|
| 313 | } |
|---|
| 314 | |
|---|
| 315 | mh_handle_request(p_server); |
|---|
| 316 | } |
|---|
| 317 | } |
|---|
| 318 | |
|---|
| 319 | mh_done(p_server); |
|---|
| 320 | return; |
|---|
| 321 | } |
|---|
| 322 | int mh_start(mh_post_cb_t func) |
|---|
| 323 | { |
|---|
| 324 | mh_server_t *p_server; |
|---|
| 325 | b_task_params task_params; |
|---|
| 326 | |
|---|
| 327 | p_server = (mh_server_t*)mh_malloc(sizeof(mh_server_t)); |
|---|
| 328 | if (!p_server) |
|---|
| 329 | return -1; |
|---|
| 330 | |
|---|
| 331 | mh_memset(p_server,0,sizeof(mh_server_t)); |
|---|
| 332 | p_server->post_cb = func; |
|---|
| 333 | p_server->listenfd = -1; |
|---|
| 334 | p_server->socketfd = -1; |
|---|
| 335 | p_server->port = DEF_SERVER_PORT; |
|---|
| 336 | mh_strcpy(p_server->path,"."); |
|---|
| 337 | |
|---|
| 338 | if (mh_startup(p_server) != 0) |
|---|
| 339 | { |
|---|
| 340 | mh_done(p_server); |
|---|
| 341 | return -1; |
|---|
| 342 | } |
|---|
| 343 | |
|---|
| 344 | /* setup the network socket */ |
|---|
| 345 | if ((p_server->listenfd = socket(AF_INET, SOCK_STREAM,0)) < 0) |
|---|
| 346 | { |
|---|
| 347 | MH_DBG_LOG(eMH_DBG_ERR,("socket failed %d",mh_geterror(0))); |
|---|
| 348 | mh_done(p_server); |
|---|
| 349 | return -1; |
|---|
| 350 | } |
|---|
| 351 | p_server->port = DEF_SERVER_PORT; |
|---|
| 352 | |
|---|
| 353 | p_server->serv_addr.sin_family = AF_INET; |
|---|
| 354 | p_server->serv_addr.sin_addr.s_addr = INADDR_ANY; |
|---|
| 355 | p_server->serv_addr.sin_port = htons(p_server->port); |
|---|
| 356 | if (bind(p_server->listenfd, (struct sockaddr *)&(p_server->serv_addr),sizeof(p_server->serv_addr)) < 0) |
|---|
| 357 | { |
|---|
| 358 | MH_DBG_LOG(eMH_DBG_ERR,("bind failed %d",mh_geterror(0))); |
|---|
| 359 | mh_done(p_server); |
|---|
| 360 | return -1; |
|---|
| 361 | } |
|---|
| 362 | |
|---|
| 363 | MH_DBG_LOG(eMH_DBG_ERR,("%s:%d\n",__FUNCTION__,__LINE__)); |
|---|
| 364 | task_params.name = "RECORD_TASK"; |
|---|
| 365 | task_params.priority = HTTPD_PRIORITY; |
|---|
| 366 | task_params.stack_size = MH_TASK_STACK_WORDS; |
|---|
| 367 | task_params.stack = p_server->task_stack; |
|---|
| 368 | bos_start_task(&(p_server->task_h), &task_params, mh_task, p_server); |
|---|
| 369 | |
|---|
| 370 | return 0; |
|---|
| 371 | } |
|---|
| 372 | |
|---|
| 373 | #ifdef WIN32 |
|---|
| 374 | int _tmain(int argc, _TCHAR* argv[]) |
|---|
| 375 | { |
|---|
| 376 | return mh_start(); |
|---|
| 377 | } |
|---|
| 378 | #endif |
|---|