/*
Esp8266 http server - core routines
*/
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* " THE BEER-WARE LICENSE " ( Revision 42 ) :
* Jeroen Domburg < jeroen @ spritesmods . com > wrote this file . As long as you retain
* this notice you can do whatever you want with this stuff . If we meet some day ,
* and you think this stuff is worth it , you can buy me a beer in return .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Modified and enhanced by Thorsten von Eicken in 2015
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# include <esp8266.h>
# include "httpd.h"
//Max length of request head
# define MAX_HEAD_LEN 1024
//Max amount of connections
# define MAX_CONN 6
//Max post buffer len
# define MAX_POST 1024
//Max send buffer len
# define MAX_SENDBUFF_LEN 2600
//This gets set at init time.
static HttpdBuiltInUrl * builtInUrls ;
//Private data for http connection
struct HttpdPriv {
char head [ MAX_HEAD_LEN ] ;
int headPos ;
char * sendBuff ;
int sendBuffLen ;
} ;
//Connection pool
static HttpdPriv connPrivData [ MAX_CONN ] ;
static HttpdConnData connData [ MAX_CONN ] ;
static HttpdPostData connPostData [ MAX_CONN ] ;
//Listening connection data
static struct espconn httpdConn ;
static esp_tcp httpdTcp ;
//Struct to keep extension->mime data in
typedef struct {
const char * ext ;
const char * mimetype ;
} MimeMap ;
//The mappings from file extensions to mime types. If you need an extra mime type,
//add it here.
static const MimeMap mimeTypes [ ] = {
{ " htm " , " text/htm " } ,
{ " html " , " text/html; charset=UTF-8 " } ,
{ " css " , " text/css " } ,
{ " js " , " text/javascript " } ,
{ " txt " , " text/plain " } ,
{ " jpg " , " image/jpeg " } ,
{ " jpeg " , " image/jpeg " } ,
{ " png " , " image/png " } ,
{ " tpl " , " text/html; charset=UTF-8 " } ,
{ NULL , " text/html " } , //default value
} ;
//Returns a static char* to a mime type for a given url to a file.
const char ICACHE_FLASH_ATTR * httpdGetMimetype ( char * url ) {
int i = 0 ;
//Go find the extension
char * ext = url + ( strlen ( url ) - 1 ) ;
while ( ext ! = url & & * ext ! = ' . ' ) ext - - ;
if ( * ext = = ' . ' ) ext + + ;
//ToDo: os_strcmp is case sensitive; we may want to do case-intensive matching here...
while ( mimeTypes [ i ] . ext ! = NULL & & os_strcmp ( ext , mimeTypes [ i ] . ext ) ! = 0 ) i + + ;
return mimeTypes [ i ] . mimetype ;
}
// debug string to identify connection (ip address & port)
// a static string works because callbacks don't get interrupted...
static char connStr [ 24 ] ;
static void debugConn ( void * arg , char * what ) {
struct espconn * espconn = arg ;
esp_tcp * tcp = espconn - > proto . tcp ;
os_sprintf ( connStr , " %d.%d.%d.%d:%d " ,
tcp - > remote_ip [ 0 ] , tcp - > remote_ip [ 1 ] , tcp - > remote_ip [ 2 ] , tcp - > remote_ip [ 3 ] ,
tcp - > remote_port ) ;
//os_printf("%s %s\n", connStr, what);
}
//Looks up the connData info for a specific esp connection
static HttpdConnData ICACHE_FLASH_ATTR * httpdFindConnData ( void * arg ) {
struct espconn * espconn = arg ;
for ( int i = 0 ; i < MAX_CONN ; i + + ) {
if ( connData [ i ] . remote_port = = espconn - > proto . tcp - > remote_port & &
os_memcmp ( connData [ i ] . remote_ip , espconn - > proto . tcp - > remote_ip , 4 ) = = 0 )
{
#if 0
os_printf ( " FindConn: 0x%p->0x%p " , arg , & connData [ i ] ) ;
if ( arg = = connData [ i ] . conn ) os_printf ( " \n " ) ;
else os_printf ( " *** was 0x%p \n " , connData [ i ] . conn ) ;
# endif
if ( arg ! = connData [ i ] . conn ) connData [ i ] . conn = arg ; // yes, this happens!?
return & connData [ i ] ;
}
}
//Shouldn't happen.
os_printf ( " %s *** Unknown connection 0x%p \n " , connStr , arg ) ;
return NULL ;
}
//Retires a connection for re-use
static void ICACHE_FLASH_ATTR httpdRetireConn ( HttpdConnData * conn ) {
conn - > conn = NULL ; // don't try to send anything, the SDK crashes...
if ( conn - > cgi ! = NULL ) conn - > cgi ( conn ) ; // free cgi data
if ( conn - > post - > buff ! = NULL ) {
os_free ( conn - > post - > buff ) ;
}
conn - > cgi = NULL ;
conn - > post - > buff = NULL ;
conn - > remote_port = 0 ;
conn - > remote_ip [ 0 ] = 0 ;
uint32 dt = conn - > startTime ;
if ( dt > 0 ) dt = ( system_get_time ( ) - dt ) / 1000 ;
os_printf ( " %s Closed, %ums, heap=%ld \n " , connStr , dt ,
( unsigned long ) system_get_free_heap_size ( ) ) ;
}
//Stupid li'l helper function that returns the value of a hex char.
static int httpdHexVal ( char c ) {
if ( c > = ' 0 ' & & c < = ' 9 ' ) return c - ' 0 ' ;
if ( c > = ' A ' & & c < = ' F ' ) return c - ' A ' + 10 ;
if ( c > = ' a ' & & c < = ' f ' ) return c - ' a ' + 10 ;
return 0 ;
}
//Decode a percent-encoded value.
//Takes the valLen bytes stored in val, and converts it into at most retLen bytes that
//are stored in the ret buffer. Returns the actual amount of bytes used in ret. Also
//zero-terminates the ret buffer.
int httpdUrlDecode ( char * val , int valLen , char * ret , int retLen ) {
int s = 0 , d = 0 ;
int esced = 0 , escVal = 0 ;
while ( s < valLen & & d < retLen ) {
if ( esced = = 1 ) {
escVal = httpdHexVal ( val [ s ] ) < < 4 ;
esced = 2 ;
} else if ( esced = = 2 ) {
escVal + = httpdHexVal ( val [ s ] ) ;
ret [ d + + ] = escVal ;
esced = 0 ;
} else if ( val [ s ] = = ' % ' ) {
esced = 1 ;
} else if ( val [ s ] = = ' + ' ) {
ret [ d + + ] = ' ' ;
} else {
ret [ d + + ] = val [ s ] ;
}
s + + ;
}
if ( d < retLen ) ret [ d ] = 0 ;
return d ;
}
//Find a specific arg in a string of get- or post-data.
//Line is the string of post/get-data, arg is the name of the value to find. The
//zero-terminated result is written in buff, with at most buffLen bytes used. The
//function returns the length of the result, or -1 if the value wasn't found. The
//returned string will be urldecoded already.
int ICACHE_FLASH_ATTR httpdFindArg ( char * line , char * arg , char * buff , int buffLen ) {
char * p , * e ;
if ( line = = NULL ) return 0 ;
p = line ;
while ( p ! = NULL & & * p ! = ' \n ' & & * p ! = ' \r ' & & * p ! = 0 ) {
//os_printf("findArg: %s\n", p);
if ( os_strncmp ( p , arg , os_strlen ( arg ) ) = = 0 & & p [ strlen ( arg ) ] = = ' = ' ) {
p + = os_strlen ( arg ) + 1 ; //move p to start of value
e = ( char * ) os_strstr ( p , " & " ) ;
if ( e = = NULL ) e = p + os_strlen ( p ) ;
//os_printf("findArg: val %s len %d\n", p, (e-p));
return httpdUrlDecode ( p , ( e - p ) , buff , buffLen ) ;
}
p = ( char * ) os_strstr ( p , " & " ) ;
if ( p ! = NULL ) p + = 1 ;
}
os_printf ( " Finding %s in %s: Not found :/ \n " , arg , line ) ;
return - 1 ; //not found
}
//Get the value of a certain header in the HTTP client head
int ICACHE_FLASH_ATTR httpdGetHeader ( HttpdConnData * conn , char * header , char * ret , int retLen ) {
char * p = conn - > priv - > head ;
p = p + strlen ( p ) + 1 ; //skip GET/POST part
p = p + strlen ( p ) + 1 ; //skip HTTP part
while ( p < ( conn - > priv - > head + conn - > priv - > headPos ) ) {
while ( * p < = 32 & & * p ! = 0 ) p + + ; //skip crap at start
//See if this is the header
if ( os_strncmp ( p , header , strlen ( header ) ) = = 0 & & p [ strlen ( header ) ] = = ' : ' ) {
//Skip 'key:' bit of header line
p = p + strlen ( header ) + 1 ;
//Skip past spaces after the colon
while ( * p = = ' ' ) p + + ;
//Copy from p to end
while ( * p ! = 0 & & * p ! = ' \r ' & & * p ! = ' \n ' & & retLen > 1 ) {
* ret + + = * p + + ;
retLen - - ;
}
//Zero-terminate string
* ret = 0 ;
//All done :)
return 1 ;
}
p + = strlen ( p ) + 1 ; //Skip past end of string and \0 terminator
}
return 0 ;
}
//Start the response headers.
void ICACHE_FLASH_ATTR httpdStartResponse ( HttpdConnData * conn , int code ) {
char buff [ 128 ] ;
int l ;
char * status = code < 400 ? " OK " : " ERROR " ;
l = os_sprintf ( buff , " HTTP/1.0 %d %s \r \n Server: esp-link \r \n Connection: close \r \n " , code , status ) ;
httpdSend ( conn , buff , l ) ;
}
//Send a http header.
void ICACHE_FLASH_ATTR httpdHeader ( HttpdConnData * conn , const char * field , const char * val ) {
char buff [ 256 ] ;
int l ;
l = os_sprintf ( buff , " %s: %s \r \n " , field , val ) ;
httpdSend ( conn , buff , l ) ;
}
//Finish the headers.
void ICACHE_FLASH_ATTR httpdEndHeaders ( HttpdConnData * conn ) {
httpdSend ( conn , " \r \n " , - 1 ) ;
}
//ToDo: sprintf->snprintf everywhere... esp doesn't have snprintf tho' :/
//Redirect to the given URL.
void ICACHE_FLASH_ATTR httpdRedirect ( HttpdConnData * conn , char * newUrl ) {
char buff [ 1024 ] ;
int l ;
l = os_sprintf ( buff , " HTTP/1.0 302 Found \r \n Server: esp8266-link \r \n Connection: close \r \n Location: %s \r \n \r \n Redirecting to %s \r \n " , newUrl , newUrl ) ;
httpdSend ( conn , buff , l ) ;
}
//Use this as a cgi function to redirect one url to another.
int ICACHE_FLASH_ATTR cgiRedirect ( HttpdConnData * connData ) {
if ( connData - > conn = = NULL ) {
//Connection aborted. Clean up.
return HTTPD_CGI_DONE ;
}
httpdRedirect ( connData , ( char * ) connData - > cgiArg ) ;
return HTTPD_CGI_DONE ;
}
//Add data to the send buffer. len is the length of the data. If len is -1
//the data is seen as a C-string.
//Returns 1 for success, 0 for out-of-memory.
int ICACHE_FLASH_ATTR httpdSend ( HttpdConnData * conn , const char * data , int len ) {
if ( len < 0 ) len = strlen ( data ) ;
if ( conn - > priv - > sendBuffLen + len > MAX_SENDBUFF_LEN ) {
os_printf ( " %s ERROR! httpdSend full (%d of %d) \n " ,
connStr , conn - > priv - > sendBuffLen , MAX_SENDBUFF_LEN ) ;
return 0 ;
}
os_memcpy ( conn - > priv - > sendBuff + conn - > priv - > sendBuffLen , data , len ) ;
conn - > priv - > sendBuffLen + = len ;
return 1 ;
}
//Helper function to send any data in conn->priv->sendBuff
static void ICACHE_FLASH_ATTR xmitSendBuff ( HttpdConnData * conn ) {
if ( conn - > priv - > sendBuffLen ! = 0 ) {
sint8 status = espconn_sent ( conn - > conn , ( uint8_t * ) conn - > priv - > sendBuff , conn - > priv - > sendBuffLen ) ;
if ( status ! = 0 ) {
os_printf ( " %s ERROR! espconn_sent returned %d \n " , connStr , status ) ;
}
conn - > priv - > sendBuffLen = 0 ;
}
}
//Callback called when the data on a socket has been successfully sent.
static void ICACHE_FLASH_ATTR httpdSentCb ( void * arg ) {
debugConn ( arg , " httpdSentCb " ) ;
int r ;
HttpdConnData * conn = httpdFindConnData ( arg ) ;
char sendBuff [ MAX_SENDBUFF_LEN ] ;
if ( conn = = NULL ) return ;
conn - > priv - > sendBuff = sendBuff ;
conn - > priv - > sendBuffLen = 0 ;
if ( conn - > cgi = = NULL ) { //Marked for destruction?
//os_printf("Closing 0x%p/0x%p->0x%p\n", arg, conn->conn, conn);
espconn_disconnect ( conn - > conn ) ; // we will get a disconnect callback
return ; //No need to call xmitSendBuff.
}
r = conn - > cgi ( conn ) ; //Execute cgi fn.
if ( r = = HTTPD_CGI_DONE ) {
conn - > cgi = NULL ; //mark for destruction.
}
if ( r = = HTTPD_CGI_NOTFOUND | | r = = HTTPD_CGI_AUTHENTICATED ) {
os_printf ( " %s ERROR! Bad CGI code %d \n " , connStr , r ) ;
conn - > cgi = NULL ; //mark for destruction.
}
xmitSendBuff ( conn ) ;
}
static const char * httpNotFoundHeader = " HTTP/1.0 404 Not Found \r \n Connection: close \r \n Content-Type: text/plain \r \n Content-Length: 12 \r \n \r \n Not Found. \r \n " ;
//This is called when the headers have been received and the connection is ready to send
//the result headers and data.
//We need to find the CGI function to call, call it, and dependent on what it returns either
//find the next cgi function, wait till the cgi data is sent or close up the connection.
static void ICACHE_FLASH_ATTR httpdProcessRequest ( HttpdConnData * conn ) {
int r ;
int i = 0 ;
if ( conn - > url = = NULL ) {
os_printf ( " %s WtF? url = NULL \n " , connStr ) ;
return ; //Shouldn't happen
}
//See if we can find a CGI that's happy to handle the request.
while ( 1 ) {
//Look up URL in the built-in URL table.
while ( builtInUrls [ i ] . url ! = NULL ) {
int match = 0 ;
//See if there's a literal match
if ( os_strcmp ( builtInUrls [ i ] . url , conn - > url ) = = 0 ) match = 1 ;
//See if there's a wildcard match
if ( builtInUrls [ i ] . url [ os_strlen ( builtInUrls [ i ] . url ) - 1 ] = = ' * ' & &
os_strncmp ( builtInUrls [ i ] . url , conn - > url , os_strlen ( builtInUrls [ i ] . url ) - 1 ) = = 0 ) match = 1 ;
if ( match ) {
//os_printf("Is url index %d\n", i);
conn - > cgiData = NULL ;
conn - > cgi = builtInUrls [ i ] . cgiCb ;
conn - > cgiArg = builtInUrls [ i ] . cgiArg ;
break ;
}
i + + ;
}
if ( builtInUrls [ i ] . url = = NULL ) {
//Drat, we're at the end of the URL table. This usually shouldn't happen. Well, just
//generate a built-in 404 to handle this.
os_printf ( " %s %s not found. 404! \n " , connStr , conn - > url ) ;
httpdSend ( conn , httpNotFoundHeader , - 1 ) ;
xmitSendBuff ( conn ) ;
conn - > cgi = NULL ; //mark for destruction
return ;
}
//Okay, we have a CGI function that matches the URL. See if it wants to handle the
//particular URL we're supposed to handle.
r = conn - > cgi ( conn ) ;
if ( r = = HTTPD_CGI_MORE ) {
//Yep, it's happy to do so and has more data to send.
xmitSendBuff ( conn ) ;
return ;
} else if ( r = = HTTPD_CGI_DONE ) {
//Yep, it's happy to do so and already is done sending data.
xmitSendBuff ( conn ) ;
conn - > cgi = NULL ; //mark conn for destruction
return ;
} else if ( r = = HTTPD_CGI_NOTFOUND | | r = = HTTPD_CGI_AUTHENTICATED ) {
//URL doesn't want to handle the request: either the data isn't found or there's no
//need to generate a login screen.
i + + ; //look at next url the next iteration of the loop.
}
}
}
//Parse a line of header data and modify the connection data accordingly.
static void ICACHE_FLASH_ATTR httpdParseHeader ( char * h , HttpdConnData * conn ) {
int i ;
char first_line = false ;
if ( os_strncmp ( h , " GET " , 4 ) = = 0 ) {
conn - > requestType = HTTPD_METHOD_GET ;
first_line = true ;
} else if ( os_strncmp ( h , " POST " , 5 ) = = 0 ) {
conn - > requestType = HTTPD_METHOD_POST ;
first_line = true ;
}
if ( first_line ) {
char * e ;
//Skip past the space after POST/GET
i = 0 ;
while ( h [ i ] ! = ' ' ) i + + ;
conn - > url = h + i + 1 ;
//Figure out end of url.
e = ( char * ) os_strstr ( conn - > url , " " ) ;
if ( e = = NULL ) return ; //wtf?
* e = 0 ; //terminate url part
// Count number of open connections
int open = 0 ;
for ( int j = 0 ; j < MAX_CONN ; j + + ) if ( connData [ j ] . conn ! = NULL ) open + + ;
os_printf ( " %s %s %s (%d conn open) \n " , connStr ,
conn - > requestType = = HTTPD_METHOD_GET ? " GET " : " POST " , conn - > url , open ) ;
//Parse out the URL part before the GET parameters.
conn - > getArgs = ( char * ) os_strstr ( conn - > url , " ? " ) ;
if ( conn - > getArgs ! = 0 ) {
* conn - > getArgs = 0 ;
conn - > getArgs + + ;
os_printf ( " %s args = %s \n " , connStr , conn - > getArgs ) ;
} else {
conn - > getArgs = NULL ;
}
} else if ( os_strncmp ( h , " Content-Length: " , 15 ) = = 0 ) {
i = 15 ;
//Skip trailing spaces
while ( h [ i ] = = ' ' ) i + + ;
//Get POST data length
conn - > post - > len = atoi ( h + i ) ;
// Allocate the buffer
if ( conn - > post - > len > MAX_POST ) {
// we'll stream this in in chunks
conn - > post - > buffSize = MAX_POST ;
} else {
conn - > post - > buffSize = conn - > post - > len ;
}
//os_printf("Mallocced buffer for %d + 1 bytes of post data.\n", conn->post->buffSize);
conn - > post - > buff = ( char * ) os_malloc ( conn - > post - > buffSize + 1 ) ;
conn - > post - > buffLen = 0 ;
} else if ( os_strncmp ( h , " Content-Type: " , 14 ) = = 0 ) {
if ( os_strstr ( h , " multipart/form-data " ) ) {
// It's multipart form data so let's pull out the boundary for future use
char * b ;
if ( ( b = os_strstr ( h , " boundary= " ) ) ! = NULL ) {
conn - > post - > multipartBoundary = b + 7 ; // move the pointer 2 chars before boundary then fill them with dashes
conn - > post - > multipartBoundary [ 0 ] = ' - ' ;
conn - > post - > multipartBoundary [ 1 ] = ' - ' ;
//os_printf("boundary = %s\n", conn->post->multipartBoundary);
}
}
}
}
//Callback called when there's data available on a socket.
static void ICACHE_FLASH_ATTR httpdRecvCb ( void * arg , char * data , unsigned short len ) {
debugConn ( arg , " httpdRecvCb " ) ;
int x ;
char * p , * e ;
char sendBuff [ MAX_SENDBUFF_LEN ] ;
HttpdConnData * conn = httpdFindConnData ( arg ) ;
if ( conn = = NULL ) return ;
conn - > priv - > sendBuff = sendBuff ;
conn - > priv - > sendBuffLen = 0 ;
//This is slightly evil/dirty: we abuse conn->post->len as a state variable for where in the http communications we are:
//<0 (-1): Post len unknown because we're still receiving headers
//==0: No post data
//>0: Need to receive post data
//ToDo: See if we can use something more elegant for this.
for ( x = 0 ; x < len ; x + + ) {
if ( conn - > post - > len < 0 ) {
//This byte is a header byte.
if ( conn - > priv - > headPos ! = MAX_HEAD_LEN ) conn - > priv - > head [ conn - > priv - > headPos + + ] = data [ x ] ;
conn - > priv - > head [ conn - > priv - > headPos ] = 0 ;
//Scan for /r/n/r/n. Receiving this indicate the headers end.
if ( data [ x ] = = ' \n ' & & ( char * ) os_strstr ( conn - > priv - > head , " \r \n \r \n " ) ! = NULL ) {
//Indicate we're done with the headers.
conn - > post - > len = 0 ;
//Reset url data
conn - > url = NULL ;
//Iterate over all received headers and parse them.
p = conn - > priv - > head ;
while ( p < ( & conn - > priv - > head [ conn - > priv - > headPos - 4 ] ) ) {
e = ( char * ) os_strstr ( p , " \r \n " ) ; //Find end of header line
if ( e = = NULL ) break ; //Shouldn't happen.
e [ 0 ] = 0 ; //Zero-terminate header
httpdParseHeader ( p , conn ) ; //and parse it.
p = e + 2 ; //Skip /r/n (now /0/n)
}
//If we don't need to receive post data, we can send the response now.
if ( conn - > post - > len = = 0 ) {
httpdProcessRequest ( conn ) ;
}
}
} else if ( conn - > post - > len ! = 0 ) {
//This byte is a POST byte.
conn - > post - > buff [ conn - > post - > buffLen + + ] = data [ x ] ;
conn - > post - > received + + ;
if ( conn - > post - > buffLen > = conn - > post - > buffSize | | conn - > post - > received = = conn - > post - > len ) {
//Received a chunk of post data
conn - > post - > buff [ conn - > post - > buffLen ] = 0 ; //zero-terminate, in case the cgi handler knows it can use strings
//Send the response.
httpdProcessRequest ( conn ) ;
conn - > post - > buffLen = 0 ;
}
}
}
}
static void ICACHE_FLASH_ATTR httpdDisconCb ( void * arg ) {
debugConn ( arg , " httpdDisconCb " ) ;
HttpdConnData * conn = httpdFindConnData ( arg ) ;
if ( conn = = NULL ) return ;
httpdRetireConn ( conn ) ;
}
// Callback indicating a failure in the connection. "Recon" is probably intended in the sense
// of "you need to reconnect". Sigh... Note that there is no DiconCb after ReconCb
static void ICACHE_FLASH_ATTR httpdReconCb ( void * arg , sint8 err ) {
debugConn ( arg , " httpdReconCb " ) ;
HttpdConnData * conn = httpdFindConnData ( arg ) ;
os_printf ( " %s ***** reset, err=%d \n " , connStr , err ) ;
if ( conn = = NULL ) return ;
httpdRetireConn ( conn ) ;
}
static void ICACHE_FLASH_ATTR httpdConnectCb ( void * arg ) {
debugConn ( arg , " httpdConnectCb " ) ;
struct espconn * conn = arg ;
int i ;
//Find empty conndata in pool
for ( i = 0 ; i < MAX_CONN ; i + + ) if ( connData [ i ] . conn = = NULL ) break ;
//os_printf("Con req, conn=%p, pool slot %d\n", conn, i);
if ( i = = MAX_CONN ) {
os_printf ( " %s Aiee, conn pool overflow! \n " , connStr ) ;
espconn_disconnect ( conn ) ;
return ;
}
#if 0
int num = 0 ;
for ( int j = 0 ; j < MAX_CONN ; j + + ) if ( connData [ j ] . conn ! = NULL ) num + + ;
os_printf ( " %s Connect (%d open) \n " , connStr , num + 1 ) ;
# endif
connData [ i ] . priv = & connPrivData [ i ] ;
connData [ i ] . conn = conn ;
connData [ i ] . remote_port = conn - > proto . tcp - > remote_port ;
os_memcpy ( connData [ i ] . remote_ip , conn - > proto . tcp - > remote_ip , 4 ) ;
connData [ i ] . priv - > headPos = 0 ;
connData [ i ] . post = & connPostData [ i ] ;
connData [ i ] . post - > buff = NULL ;
connData [ i ] . post - > buffLen = 0 ;
connData [ i ] . post - > received = 0 ;
connData [ i ] . post - > len = - 1 ;
connData [ i ] . startTime = system_get_time ( ) ;
espconn_regist_recvcb ( conn , httpdRecvCb ) ;
espconn_regist_reconcb ( conn , httpdReconCb ) ;
espconn_regist_disconcb ( conn , httpdDisconCb ) ;
espconn_regist_sentcb ( conn , httpdSentCb ) ;
espconn_set_opt ( conn , ESPCONN_REUSEADDR | ESPCONN_NODELAY ) ;
}
//Httpd initialization routine. Call this to kick off webserver functionality.
void ICACHE_FLASH_ATTR httpdInit ( HttpdBuiltInUrl * fixedUrls , int port ) {
int i ;
for ( i = 0 ; i < MAX_CONN ; i + + ) {
connData [ i ] . conn = NULL ;
}
httpdConn . type = ESPCONN_TCP ;
httpdConn . state = ESPCONN_NONE ;
httpdTcp . local_port = port ;
httpdConn . proto . tcp = & httpdTcp ;
builtInUrls = fixedUrls ;
os_printf ( " Httpd init, conn=%p \n " , & httpdConn ) ;
espconn_regist_connectcb ( & httpdConn , httpdConnectCb ) ;
espconn_accept ( & httpdConn ) ;
espconn_tcp_set_max_con_allow ( & httpdConn , MAX_CONN ) ;
}