lwip-1.4.1 (partial)

Dependents:   IGLOO_board

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers http_req.c Source File

http_req.c

00001 /*
00002  * The MIT License (MIT)
00003  *
00004  * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
00005  * 
00006  * Permission is hereby granted, free of charge, to any person obtaining a copy
00007  * of this software and associated documentation files (the "Software"), to deal
00008  * in the Software without restriction, including without limitation the rights
00009  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00010  * copies of the Software, and to permit persons to whom the Software is
00011  * furnished to do so, subject to the following conditions:
00012  * 
00013  * The above copyright notice and this permission notice shall be included in all
00014  * copies or substantial portions of the Software.
00015  * 
00016  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00019  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00020  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00021  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00022  * SOFTWARE.
00023  */
00024 
00025 /*
00026  * version: 1.0 demo (7.02.2015)
00027  * brief:   part of tiny http ipv4 server using lwip (pcb)
00028  */
00029 
00030 #include "http_req.h"
00031 
00032 #ifdef __cplusplus
00033 extern "C" {
00034 #endif
00035 
00036 const char MIME_TEXT_HTML[] =   "text/html";
00037 const char MIME_TEXT_JS[] =     "text/javascript";
00038 const char MIME_TEXT_PLAIN[] =  "text/plain";
00039 const char MIME_TEXT_XML[] =    "text/xml";
00040 const char MIME_TEXT_CSS[] =    "text/css";
00041 const char MIME_IMAGE_GIF[] =   "image/gif";
00042 const char MIME_IMAGE_JPEG[] =  "image/jpeg";
00043 const char MIME_IMAGE_PJPEG[] = "image/pjpeg";
00044 const char MIME_IMAGE_PNG[] =   "image/png";
00045 const char MIME_IMAGE_SVG[] =   "image/svg+xml";
00046 const char MIME_IMAGE_TIFF[] =  "image/tiff";
00047 const char MIME_IMAGE_ICON[] =  "image/vnd.microsoft.icon";
00048 const char MIME_IMAGE_WBMP[] =  "image/vnd.wap.wbmp";
00049 
00050 static const char *METHODS_STR[] =
00051 {
00052     "",
00053     "GET",
00054     "POST",
00055     "HEAD",
00056     "PUT",
00057     "CONNECT",
00058     "OPTIONS",
00059     "DELETE",
00060     "TRACE",
00061     "PATCH"
00062 };
00063 
00064 static const char *CONN_TYPE_STR[] =
00065 {
00066     "", "close", "keep-alive"
00067 };
00068 
00069 void http_reqb_init(http_reqb_t *reqb, void *buff, int size)
00070 {
00071     memset(reqb, 0, sizeof(http_reqb_t));
00072     reqb->buff = buff;
00073     reqb->bsize = size;
00074 }
00075 
00076 int http_reqb_avail(const http_reqb_t *reqb)
00077 {
00078     return reqb->bsize - reqb->size;
00079 }
00080 
00081 static chatset_t req_ident =
00082 {
00083     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00084     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
00085     0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, // '%', '-', '.', '/'
00086     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // '0'...'9'
00087     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'A'...'O'
00088     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 'P'...'Z', '_'
00089     0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 'a'...'z'
00090     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 'p'...'z'
00091 };
00092 
00093 static http_mt_t str_to_method(const char *str)
00094 {
00095     http_mt_t res;
00096     for (res = METHOD_GET; res <= METHOD_PATCH; res++)
00097         if (strcmp(METHODS_STR[res], str) == 0)
00098             return res;
00099     return METHOD_NONE;
00100 }
00101 
00102 static http_ct_t str_to_ct(const char *str)
00103 {
00104     if (strcmp(str, CONN_TYPE_STR[CT_CLOSE]) == 0) return CT_CLOSE;
00105     if (strcmp(str, CONN_TYPE_STR[CT_KEEP_ALIVE]) == 0) return CT_KEEP_ALIVE;
00106     return CT_NONE;
00107 }
00108 
00109 void http_req_set_param(http_req_t *req, char *name, char *val)
00110 {
00111     if (strcmp(name, "Host") == 0)
00112         req->host = val;
00113     else
00114     if (strcmp(name, "User-Agent") == 0)
00115         req->user_agent = val;
00116     else
00117     if (strcmp(name, "Content-Type") == 0)
00118         req->mime = val;
00119     else
00120     if (strcmp(name, "Content-Length") == 0)
00121         req->cont_len = strtol(val, NULL, 10);
00122     else
00123     if (strcmp(name, "Accept") == 0)
00124         req->accept = val;
00125     else
00126     if (strcmp(name, "Accept-Language") == 0)
00127         req->accept_lang = val;
00128     else
00129     if (strcmp(name, "Accept-Encoding") == 0)
00130         req->accept_enc = val;
00131     else
00132     if (strcmp(name, "Cookie") == 0)
00133         req->cookie = val;
00134     else
00135     if (strcmp(name, "Connection") == 0)
00136         req->conn_type = str_to_ct(val);
00137     else
00138     if (strcmp(name, "Keep-Alive") == 0)
00139         req->keep_alive = strtol(val, NULL, 10);
00140 }
00141 
00142 void http_reqb_push(http_reqb_t *rb, const void *data, int size)
00143 {
00144     parser_t p;
00145     int indeces[4];
00146 
00147     if (rb->size + size >= rb->bsize)
00148     {
00149         rb->state = REQB_REQ_TO_BIG;
00150         return;
00151     }
00152     if (rb->state != REQB_UNFINISHED) return;
00153 
00154     memcpy(&rb->buff[rb->size], data, size);
00155     rb->size += size;
00156 
00157     parser_init(&p, rb->buff, rb->size);
00158     parser_skip_n(&p, rb->parsing.pos);
00159 
00160     while (!parser_eof(&p))
00161     {
00162         int total;
00163         switch (rb->parsing.state)
00164         {
00165         case 0:
00166             parser_skip_ws(&p);
00167             total = p.total;
00168             parser_skip_before(&p, ' ');
00169             if (parser_eof(&p)) break;
00170             rb->buff[p.total] = 0;
00171             rb->req.method = str_to_method(&rb->buff[total]);
00172             parser_skip_n(&p, 1);
00173             rb->parsing.pos = p.total;
00174             rb->parsing.state = 1;
00175             break;
00176         case 1:
00177             parser_skip_ws(&p);
00178             total = p.total;
00179             parser_skip(&p, req_ident);
00180             if (parser_eof(&p)) break;
00181             switch (parser_curr(&p))
00182             {
00183             case '?': rb->parsing.state = 2; break;
00184             case ' ': rb->parsing.state = 4; break;
00185             default:  rb->parsing.state = 11; break;
00186             }
00187             if (rb->parsing.state >= 10) break; // error
00188             rb->req.uri = &rb->buff[total];
00189             rb->buff[p.total] = 0;
00190             parser_skip_n(&p, 1);
00191             rb->parsing.pos = p.total;
00192             break;
00193         case 2:
00194             total = p.total;
00195             parser_skip(&p, req_ident);
00196             if (parser_eof(&p)) break;
00197             switch (parser_curr(&p))
00198             {
00199             case '&': rb->parsing.state = 2; break;
00200             case '=': rb->parsing.state = 3; break;
00201             case ' ': rb->parsing.state = 4; break;
00202             default:  rb->parsing.state = 12; break;
00203             }
00204             if (rb->parsing.state >= 10) break; // error
00205             rb->req.num_params++;
00206             if (rb->req.num_params <= HTTP_REQ_MAX_PARAMS)
00207             {
00208                 rb->req.params[rb->req.num_params - 1] = &rb->buff[total];
00209                 rb->req.values[rb->req.num_params - 1] = NULL;
00210             }
00211             rb->buff[p.total] = 0;
00212             parser_skip_n(&p, 1);
00213             rb->parsing.pos = p.total;
00214             break;
00215         case 3:
00216             total = p.total;
00217             parser_skip(&p, req_ident);
00218             if (parser_eof(&p)) break;
00219             switch (parser_curr(&p))
00220             {
00221             case '&': rb->parsing.state = 2; break;
00222             case ' ': rb->parsing.state = 4; break;
00223             default:  rb->parsing.state = 13; break;
00224             }
00225             if (rb->parsing.state >= 10) break; // error
00226             if (rb->req.num_params <= HTTP_REQ_MAX_PARAMS)
00227                 rb->req.values[rb->req.num_params - 1] = &rb->buff[total];
00228             rb->buff[p.total] = 0;
00229             parser_skip_n(&p, 1);
00230             rb->parsing.pos = p.total;
00231             break;
00232         case 4:
00233             parser_skip_ws(&p);
00234             total = p.total;
00235             if (!parser_skip_line(&p)) break;
00236             rb->req.ver = &rb->buff[total];
00237             rb->buff[p.total - 2] = 0;
00238             rb->parsing.pos = p.total;
00239             rb->parsing.state = 5;
00240             break;
00241         case 5:
00242             parser_skip_ws_in_line(&p);
00243             if (parser_skip_char(&p, '\r') || parser_skip_char(&p, '\n'))
00244             {
00245                 // DONE
00246                 rb->state = REQB_FINISHED;
00247                 return;
00248             }
00249             // 0     1   2            3
00250             // Accept:   text/html\r\n...
00251             parser_skip_ws_in_line(&p);
00252             indeces[0] = p.total;
00253             parser_skip(&p, req_ident);
00254             if (parser_eof(&p)) break;
00255             if (parser_curr(&p) != ':')
00256             {
00257                 rb->parsing.state = 15;
00258                 break;
00259             }
00260             indeces[1] = p.total;
00261             parser_skip_n(&p, 1);
00262             parser_skip_ws_in_line(&p);
00263             indeces[2] = p.total;
00264             if (!parser_skip_line(&p)) break;
00265             indeces[3] = p.total;
00266             rb->buff[indeces[1]] = 0;
00267             rb->buff[indeces[3] - 2] = 0;
00268             http_req_set_param(&rb->req,
00269                 &rb->buff[indeces[0]],
00270                 &rb->buff[indeces[2]]);
00271             rb->parsing.pos = p.total;
00272             break;
00273         default:
00274             rb->state = REQB_SYNT_ERROR;
00275             return;
00276         }
00277     }
00278 }
00279 
00280 int http_resp_len(const http_resp_t *resp)
00281 {
00282     char temp[1];
00283     return http_resp_str(resp, temp, 1);
00284 }
00285 
00286 int http_resp_str(const http_resp_t *resp, char *str, int size)
00287 {
00288     return snprintf(str, size,
00289         "HTTP/1.1 %d\r\n"
00290         "Server: %s\r\n"
00291 //      "Content-Type: text/html\r\n"
00292         "Content-Type: %s\r\n"
00293         "Content-Length: %d\r\n"
00294         "Connection: %s\r\n"
00295         "\r\n",
00296         resp->code,
00297         resp->server,
00298         resp->mime,
00299         resp->cont_len,
00300         CONN_TYPE_STR[resp->conn_type]);
00301 }
00302 
00303 #ifdef __cplusplus
00304 }
00305 #endif