/******************************************************************************
* Copyright ? 2004 Altera Corporation, San Jose, California, USA.             *
* All rights reserved. All use of this software and documentation is          *
* subject to the License Agreement located at the end of this file below.     *
*******************************************************************************
* Author - PRR/JRK                                                            *
*                                                                             *
* File: http.c                                                                *
*                                                                             *
* A rough imlementation of HTTP. This is not intended to be a complete        *
* implementation, just enough for a demo simple web server. This example      *
* application is more complex than the telnet serer example in that it uses   *
* non-blocking IO & multiplexing to allow for multiple simultaneous HTTP      *
* sessions.                                                                   *            
*                                                                             *
* LWIP has two API's, a callback interface and "standard" sockets this        *
* example uses the sockets interface. A good introduction to sockets          *
* programming is the book Unix Network Programming by Richard Stevens         *
*                                                                             *
* Please refer to file readme.txt for notes on this software example.         *
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/fcntl.h>
#include "lwip/netif.h"
#include "lwip/sockets.h"
#include "arch/sys_arch.h"
#include "sys/alt_alarm.h"
#include "alt_types.h"
#include "http.h"
#include "user.h"


// ++ hychu
#if 0
struct __sFILE {
  unsigned char *_p;	/* current position in (some) buffer */
  int	_r;		/* read space left for getc() */
  int	_w;		/* write space left for putc() */
  short	_flags;		/* flags, below; this FILE is free if 0 */
  short	_file;		/* fileno, if Unix descriptor, else -1 */
  struct __sbuf _bf;	/* the buffer (at least 1 byte, if !NULL) */
  int	_lbfsize;	/* 0 or -_bf._size, for inline putc */

  /* operations */
  _PTR	_cookie;	/* cookie passed to io functions */

  _READ_WRITE_RETURN_TYPE _EXFUN((*_read),(_PTR _cookie, char *_buf, int _n));
  _READ_WRITE_RETURN_TYPE _EXFUN((*_write),(_PTR _cookie, const char *_buf,					    int _n));
  _fpos_t _EXFUN((*_seek),(_PTR _cookie, _fpos_t _offset, int _whence));
  int	_EXFUN((*_close),(_PTR _cookie));

  /* separate buffer for long sequences of ungetc() */
  struct __sbuf _ub;	/* ungetc buffer */
  unsigned char *_up;	/* saved _p when _p is doing ungetc data */
  int	_ur;		/* saved _r when _r is counting ungetc data */

  /* tricks to meet minimum requirements even when malloc() fails */
  unsigned char _ubuf[3];	/* guarantee an ungetc() buffer */
  unsigned char _nbuf[1];	/* guarantee a getc() buffer */

  /* separate buffer for fgetline() when line crosses buffer boundary */
  struct __sbuf _lb;	/* buffer for fgetline() */

  /* Unix stdio files get aligned to block boundaries on hyseek() */
  int	_blksize;	/* stat.st_blksize (may be != _bf._size) */
  int	_offset;	/* current lseek offset */

#ifndef __SINGLE_THREAD__
  _flock_t _lock;	/* for thread-safety locking */
#endif
};
#endif

#define	MAX_CONCURRENT_HYPER	64
#define	HYPER_FID_START			0x7000
FILE	_gFiles[MAX_CONCURRENT_HYPER];
int _ghy_initd = 0;

#define HY_CHK_FP(fp)	((fp) && ((fp)->_file >= HYPER_FID_START))

FILE * hy_allocate_fp(void)
{
	int i;
	for(i=0;i<MAX_CONCURRENT_HYPER;i++)
	{
		if(_gFiles[i]._flags != 0) continue;
		_gFiles[i]._flags = 0xbeef;
		return &_gFiles[i];
	} // of for i
	
	return NULL;
}

void hy_release_fp(FILE *fp)
{
	fp->_flags = 0;
}

FILE * hyopen(const char * pfname, const char * pmode)
{
	// bypass if ordinary file
	if(strncmp(pfname+strlen(ALTERA_RO_ZIPFS_NAME), "/file?", 5)) return fopen(pfname, pmode);

	// initialization
	if(!_ghy_initd)
	{
		int i;
		_ghy_initd = 1;
		for(i=0;i<MAX_CONCURRENT_HYPER;i++)
		{
			_gFiles[i]._file = HYPER_FID_START + i;
			_gFiles[i]._flags = 0;
		} // of for i
	} // of not init'd
	
	FILE * fp;
	
	if(!(fp = hy_allocate_fp())) return NULL;
	
	int flen=0;
	char * ptr;
	
	// seek for size
	if((ptr=strstr(pfname, "size="))) flen = atoi(ptr+5);
	
	// seek for unit
	for(ptr+=5; *ptr ; ptr++)
		if(*ptr < '0' || *ptr > '9') break;
	
	switch (*ptr)
	{
		case 'k': // 1000
			flen *= 1000;
			break;
		case 'K': // 1024
			flen *= 1024;
			break;
		case 'm': // 1,000,000
			flen *= 1000000;
			break;
		case 'M': // 1024*1024
			flen *= 1024*1024;
			break;
		default:
			break;
	} // of switch
	
	fp->_cookie = flen ? (void*)flen:(void*)(1024*1024);
	
	return fp;
}

int	hygetpos(FILE * fp, fpos_t * offset)
{
	if(!HY_CHK_FP(fp)) return fgetpos(fp, offset);
	
	*offset = (int)fp->_offset;
	
	return 0;
}

int	hyseek(FILE * fp, long offset, int origin)
{
	if(!HY_CHK_FP(fp)) return fseek(fp, offset, origin);
	
	switch(origin)
	{
		case SEEK_SET:
			fp->_offset = offset;
			break;
			
		case SEEK_CUR: // is it right ?
			fp->_offset += offset;
			break;
			
		case SEEK_END:
			fp->_offset = (int)(fp->_cookie) + offset;
			break;
			
		default:
			return -1;
	} // of switch(origin)
	
	return 0;
}


int	hyclose(FILE * fp)
{
	if(!HY_CHK_FP(fp)) return fclose(fp);
	
	hy_release_fp(fp);
	
	return 0;
}

int hyread(void * pbuffer, size_t size, size_t count, FILE * fp)
{
	if(!HY_CHK_FP(fp)) return fread(pbuffer, size, count, fp);
	
	int total, remains;
	
	remains = (int)(fp->_cookie) - fp->_offset;
	total = (remains >= size*count)? size*count:remains;
	fp->_offset += total;
	return total;
}

// -- hychu

#ifdef DEBUG1
  #include alt_debug.h
#else
  #define ALT_DEBUG_ASSERT(a) 
#endif /* DEBUG */

/* 
 * TX & RX buffers. 
 * 
 * These are declared globally to prevent the MicroC/OS-II thread from
 * consuming too much OS-stack space
 */
alt_u8 http_rx_buffer[HTTP_NUM_CONNECTIONS][HTTP_RX_BUF_SIZE];
alt_u8 http_tx_buffer[HTTP_NUM_CONNECTIONS][HTTP_TX_BUF_SIZE];

/* 
 * This canned HTTP reply will serve as a "404 - Not Found" Web page. HTTP
 * headers and HTML embedded into the single string.
 */
static const alt_8 canned_http_response[] = {"\
HTTP/1.0 404 Not Found\r\n\
Content-Type: text/html\r\n\
Content-Length: 272\r\n\r\n\
<HTML><HEAD><TITLE>Nios II Web Server Demonstration</TITLE></HEAD>\
<title>LWIP on Nios II</title><BODY><h1>HTTP Error 404</h1>\
<center><h2>Nios II Web Server Demonstration</h2>\
Can't find the requested file file. \
Have you programmed the flash filing system into flash?</html>\
"};

// hychu
static const alt_8 hello_http_response[] = {"\
HTTP/1.0 200\r\n\
Content-Type: text/html\r\n\
Content-Length: 203\r\n\r\n\
<HTML><HEAD><TITLE>Nios II Web Server Demonstration</TITLE></HEAD>\
<title>LWIP on Nios II</title>\
<BODY>\
<br><br>\
<center>\
<h1>Nios II Web Server Demonstration</h1><br>\
<h2>Hello World</h2><br>\
</center>\
</html>\
"};

/* 
 * Mapping between pages to post to and functions to call: This allows us to
 * have the HTTP server respond to a POST requset saying "print" by calling
 * a "print" routine (below).
 */
typedef struct funcs
{
  alt_u8*  name;
  void (*func)(); 
}post_funcs;

/* 
 * print()
 * 
 * This routine is called to demonstrate doing something server-side when an
 * HTTP "POST" command is received.
 */
void print()
{
  printf("HTTP POST received.\n");
}

/* 
 * The mapping (using our struct defined above) between HTTP POST commands
 * that we will service and the subroutine called to service that POST
 * command.
 */
post_funcs mapping =
{
  "/PRINT",
  print
};

/*
 * http_reset_connection()
 * 
 * This routine will clear our HTTP connection structure & prepare it to handle
 * a new HTTP connection.
 */
void http_reset_connection(http_conn* conn, int http_instance)
{
  memset(conn, 0, sizeof(http_conn));

  conn->fd = -1;
  conn->state = READY;
  conn->keep_alive_count = HTTP_KEEP_ALIVE_COUNT;
  
  conn->rx_buffer = (alt_u8 *) &http_rx_buffer[http_instance][0];
  conn->tx_buffer = (alt_u8 *) &http_tx_buffer[http_instance][0];
  conn->rx_wr_pos = (alt_u8 *) &http_rx_buffer[http_instance][0];
  conn->rx_rd_pos = (alt_u8 *) &http_rx_buffer[http_instance][0];
}

/*
 * http_manage_connection()
 * 
 * This routine performs house-keeping duties for a specific HTTP connection
 * structure. It is called from various points in the HTTP server code to
 * ensure that connections are reset properly on error, completion, and
 * to ensure that "zombie" connections are dealt with.
 */
void http_manage_connection(http_conn* conn, int http_instance)
{
  alt_u32 current_time = 0;
  
  /*
   * Keep track of whether an open connection has timed out. This will be
   * determined by comparing the current time with that of the most recent
   * activity.
   */
  if(conn->state == READY || conn->state == PROCESS || conn->state == DATA)
  {
    current_time = alt_nticks();
    
    if( (current_time - conn->activity_time) >= HTTP_KEEP_ALIVE_TIME )
    {
      conn->state = RESET;
    }
  }
  
  /*
   * The reply has been sent. Is is time to drop this connection, or 
   * should we persist? We'll keep track of these here and mark our
   * state machine as ready for additional connections... or not.
   *  - Only send so many files per connection. 
   *  - Stop when we reach a timeout.
   *  - If someone (like the client) asked to close the connection, do so.
   */
  if(conn->state == COMPLETE)
  {
    if(conn->file_handle != NULL)
    {
      hyclose(conn->file_handle);
    }
      
    conn->keep_alive_count--;
    conn->data_sent = 0;
  
    if(conn->keep_alive_count == 0)
    {
      conn->close = 1;
    }
    
    conn->state = conn->close ? CLOSE : READY;
  }
  
  /* 
   * Some error occured. http_reset_connection() will take care of most
   * things, but the RX buffer still needs to be cleared, and any open
   * files need to be closed. We do this in a separate state to maintain 
   * efficiency between successive (error-free) connections.
   */
  if(conn->state == RESET)
  {
    if(conn->file_handle != NULL)
    {
      hyclose(conn->file_handle);
    }
      
    memset(conn->rx_buffer, 0, HTTP_RX_BUF_SIZE);
    conn->state = CLOSE;
  }
  
  /* Close the TCP connection */
  if(conn->state == CLOSE)
  {
    close(conn->fd);
    http_reset_connection(conn, http_instance);
  }
}

/*
 * http_handle_accept()
 * 
 * A listening socket has detected someone trying to connect to us. If we have 
 * any open connection slots we will accept the connection (this creates a 
 * new socket for the data transfer), but if all available connections are in 
 * use we'll ignore the client's incoming connection request.
 */
int http_handle_accept(int listen_socket, http_conn* conn)
{
  int ret_code = 0, i, socket, len;
  struct sockaddr_in  rem;

  len = sizeof(rem);

  /* 
   * Loop through available connection slots to determine the first available
   * connection.
   */
  for(i=0; i<HTTP_NUM_CONNECTIONS; i++)
  {
    if((conn+i)->fd == -1)
    {
      break;
    }
  }
  
  /* 
   * There are no more connection slots available. Ignore the connection
   * request for now.
   */
  if(i == HTTP_NUM_CONNECTIONS)
    return -1;
      
  if((socket = accept(listen_socket,(struct sockaddr*)&rem,&len)) < 0)
  {
    //fprintf(stderr, "[http_handle_accept] accept failed (%d)\n", socket);
    return socket;
  }

  (conn+i)->fd = socket;
  (conn+i)->activity_time = alt_nticks();

  return ret_code;
}

/*
 * http_read_line()
 * 
 * This routine will scan the RX data buffer for a newline, allowing us to
 * parse an in-coming HTTP request line-by-line.
 */
int http_read_line(http_conn* conn)
{
  alt_u8* lf_addr;
  int ret_code = 0;

  /* Find the Carriage return which marks the end of the header */
  lf_addr = strchr(conn->rx_rd_pos, '\n');
  
  if (lf_addr == NULL)
  {
    ret_code = -1;
  }
  else
  {
    /* 
     * Check that the line feed has a matching CR, if so zero that
     * else zero the LF so we can use the string searching functions.
     */
    if ((lf_addr > conn->rx_buffer) && (*(lf_addr-1) == '\r'))
    {
      *(lf_addr-1) = 0;
    }

    *lf_addr = 0;
    conn->rx_rd_pos = lf_addr+1;
  }

  return ret_code;
}

/*
 * http_process_headers()
 * 
 * This routine looks for HTTP header commands, specified by a ":" character.
 * We will look for "Connection: Close" and "Content-length: <len>" strings. 
 * A more advanced server would parse far more header information.
 * 
 * This routine should be modified in the future not to use strtok() as its
 * a bit invasive and is not thread-safe!
 */
int http_process_headers(http_conn* conn)
{
  alt_u8* option;
  alt_u8* orig_read_pos = conn->rx_rd_pos;
  alt_u8* temp;
  
  /* Skip the next section we'll chop with strtok(). Perl for Nios, anyone? */
  if( (temp = strstr(conn->rx_rd_pos, ":")) )
  {
    conn->rx_rd_pos = temp + 1;
  }
  else
  {
    return -1;
  }
    
  option = strtok(orig_read_pos, ":");

  if(stricmp(option,"Connection") == 0)
  {
    alt_u8 temp = *(option + 17);
    *(option + 17) = 0;
    
    if(stricmp((option+12), "close") == 0)
    {
      conn->close = 1;
    }
    *(option + 17) = temp;
  }
  else if (stricmp(option, "Content-Length") == 0)
  {
   conn->content_length = atoi(option+16);
  }

  return 0;
}

/*
 * http_process_request()
 * 
 * This routine parses the beginnings of an HTTP request to extract the
 * command, version, and URI. Unsupported commands/versions/etc. will cause
 * us to error out drop the connection.
 */
int http_process_request(http_conn* conn)
{
  alt_u8* uri = 0;
  alt_u8* version = 0;
  alt_u8* temp = 0;
  if( (temp = strstr(conn->rx_rd_pos, "GET")) )
  {
    conn->action = GET;
    conn->rx_rd_pos = temp;
  }
  else if( (temp = strstr(conn->rx_rd_pos, "POST")) )
  {
    conn->action = POST;
    conn->rx_rd_pos = temp;
  }
  else
  {
    //fprintf(stderr, "Unsupported (for now) request\n");
    conn->action = UNKNOWN;
    return -1;
  }
  
  /* First space char separates action from URI */
  if( (conn->rx_rd_pos = strstr(conn->rx_rd_pos, " ")) )
  {
    conn->rx_rd_pos++;
    uri = conn->rx_rd_pos;
  }
  else
  {
    return -1;
  }
    
  /* Second space char separates URI from HTTP version. */
  if( (conn->rx_rd_pos = strstr(conn->rx_rd_pos, " ")) )
  {
    *conn->rx_rd_pos = 0;
    conn->rx_rd_pos++;
    version = conn->rx_rd_pos;
  }
  else
  {
    return -1;
  }
  
  /* Is this a HTTP version we support? */
  if ((version == NULL) || (strncmp(version, "HTTP/", 5) != 0))
  {
    return -1;
  }

  if (!isdigit(version[5]) || version[6] != '.' || !isdigit(version[7]))
  {
    return -1;
  }

  /* Before v1.1 we close the connection after responding to the request */
  if ( (((version[5] - '0')*10) + version[7] - '0') < 11)
  {
    conn->close = 1;
  }

  strcpy(conn->uri, uri);
  return 0;
}

/*
 * http_send_file_chunk()
 * 
 * This routine will send the next chunk of a file during an open HTTP session
 * where a file is being sent back to the client. This routine is called 
 * repeatedly until the file is completely sent, at which time the connection
 * state will go to "COMPLETE". Doing this rather than sending the entire
 * file allows us (in part) to multiplex between connections "simultaneously".
 */
int http_send_file_chunk(http_conn* conn)
{
  int chunk_sent = 0, ret_code = 0, file_chunk_size = 0, result = 0;
  alt_u8* tx_ptr;
  
  if(conn->data_sent < conn->file_length)
  {
    file_chunk_size = hyread(conn->tx_buffer, 1, 
      MIN(HTTP_TX_BUF_SIZE, (conn->file_length - conn->data_sent)), 
      conn->file_handle);
    
    tx_ptr = conn->tx_buffer;
    
    while(chunk_sent < file_chunk_size)
    {
      result = send(conn->fd, tx_ptr, file_chunk_size, MSG_DONTWAIT);
      
      /* Error - get out of here! */
      if(result < 0)
      {
        //fprintf(stderr, "[http_send_file] file send returned %d\n", result);
        ALT_DEBUG_ASSERT(1);
        conn->state = RESET;
        return result;
      }

      /*
       * No errors, but the number of bytes sent might be less than we wanted.
       */
      else
      {
        conn->activity_time = alt_nticks();
        chunk_sent += result;
        conn->data_sent += result;
        tx_ptr += result;
        file_chunk_size -= result;
      }
    } /* while(chunk_sent < file_chunk_size) */
  } /* if(conn->data_sent < conn->file_length) */
  
  /* 
   * We managed to send all of the file contents to the IP stack successfully.
   * At this point we can mark our connection info as complete.
   */
  if(conn->data_sent >= conn->file_length)
  {
    conn->state = COMPLETE;
  }

  return ret_code;
}

/*
 * http_send_file_header()
 *
 * Construct and send an HTTP header describing the now-opened file that is
 * about to be sent to the client.
 */
int http_send_file_header(http_conn* conn, const alt_u8* name, int code)
{
  int     result = 0, ret_code = 0;
  alt_u8* tx_wr_pos = conn->tx_buffer;
  fpos_t  end, start;
	const alt_u8* ext = strchr(name, '.');

  tx_wr_pos += sprintf(tx_wr_pos, HTTP_VERSION_STRING);

  switch(code)
  {
    /* HTTP Code: "200 OK\r\n" (we have opened the file successfully) */
    case HTTP_OK:
    {
      tx_wr_pos += sprintf(tx_wr_pos, HTTP_OK_STRING);
      break;
    }
    /* HTTP Code: "404 Not Found\r\n" (couldn't find requested file) */
    case HTTP_NOT_FOUND:
    {
      tx_wr_pos += sprintf(tx_wr_pos, HTTP_NOT_FOUND_STRING);
      break;
    }
    default:
    {
      //fprintf(stderr, "[http_send_file_header] Invalid HTTP code: %d\n", code);
      conn->state = RESET;
      return -1;
      break;
    }
  }

  /* Handle the various content types */
  tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_TYPE);

  if (!strcasecmp(ext, ".html"))
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_TYPE_HTML);
  }
  else if (!strcasecmp(ext, ".jpg"))
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_TYPE_JPG);
  }
  else if (!strcasecmp(ext, ".gif"))
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_TYPE_GIF);
  }
  else if (!strcasecmp(ext, ".js"))
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_TYPE_JS);
  }
  else if (!strcasecmp(ext, ".css"))
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_TYPE_CSS);
  }
  else if (!strcasecmp(ext, ".swf"))
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_TYPE_SWF);
  }
  else
// ++ hychu
	{
		tx_wr_pos += sprintf(tx_wr_pos, "application/x-javascript\r\n");
	} // of unknown content type
	
#if 0
  {
    //fprintf(stderr, "[http_send_file] Unknown content type: \"%s\"\n", ext);
    conn->state = RESET;
    ALT_DEBUG_ASSERT(1);
    return -1;
  }
#endif

  /* Get the file length and stash it into our connection info */
  hyseek(conn->file_handle, 0, SEEK_END);
  hygetpos(conn->file_handle, &end);
  hyseek(conn->file_handle, 0, SEEK_SET);
  hygetpos(conn->file_handle, &start);
  conn->file_length = end - start;

  /* "Content-Length: <length bytes>\r\n" */
  tx_wr_pos += sprintf(tx_wr_pos, HTTP_CONTENT_LENGTH);
  tx_wr_pos += sprintf(tx_wr_pos, "%d\r\n", conn->file_length);

  /* 
   * 'close' will be set during header parsing if the client either specified
   * that they wanted the connection closed ("Connection: Close"), or if they
   * are using an HTTP version prior to 1.1. Otherwise, we will keep the 
   * connection alive. 
   * 
   * We we send a specified number of files in a single keep-alive connection,
   * we'll also close the connection. Its best to be polite and tell the client,
   * though.
   */
  if(!conn->keep_alive_count)
  {
    conn->close = 1;
  }
  
  if(conn->close)
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_CLOSE);
  }
  else
  {
    tx_wr_pos += sprintf(tx_wr_pos, HTTP_KEEP_ALIVE);
  }

  /* "\r\n" (two \r\n's in a row means end of headers */
  tx_wr_pos += sprintf(tx_wr_pos, HTTP_CR_LF);

  /* Send the reply header */
  result = send(conn->fd, conn->tx_buffer, (tx_wr_pos - conn->tx_buffer), 
                MSG_DONTWAIT);  
                
  if(result < 0)
  {
    //fprintf(stderr, "[http_send_file] header send returned %d\n", result);
    conn->state = RESET;
    return result;
  }
  else
  {
    conn->activity_time = alt_nticks();
  }
  
  return ret_code;
}


/*
 * http_find_file()
 *
 * Try to find the file requested. If nothing is requested you get /index.html
 * If we can't find it, send a "404 - Not found" message.
 */
int http_find_file(http_conn* conn)
{
  alt_u8  filename[256];
  int     ret_code = 0;

  strncpy( filename, ALTERA_RO_ZIPFS_NAME, strlen(ALTERA_RO_ZIPFS_NAME));

  /* URI of "/" means get the default, usually index.html */
  if ( (conn->uri[0] == '/') && (conn->uri[1] == '\0') )
  {
    strcpy(filename+strlen(ALTERA_RO_ZIPFS_NAME), HTTP_DEFAULT_FILE);
  }
  else
  {
    strcpy( filename+strlen(ALTERA_RO_ZIPFS_NAME), conn->uri);
  }
  
// ++ hychu
  /* Try to open the file */
  // was: conn->file_handle = fopen(filename, "r");
	//if(!strncmp(filename, "file?", 5))
	conn->file_handle = hyopen(filename, "r");
	//else		conn->file_handle = fopen(filename, "r");
// -- hychu

  /* Can't find the requested file? Try for a 404-page. */
  if (conn->file_handle == NULL)
  {
    strcpy(filename, ALTERA_RO_ZIPFS_NAME);
    strcpy(filename+strlen(ALTERA_RO_ZIPFS_NAME), HTTP_NOT_FOUND_FILE);
    conn->file_handle = hyopen(filename, "r");
    
    /* We located the specified "404: Not-Found" page */
    if (conn->file_handle != NULL)
    {
      ALT_DEBUG_ASSERT(fd != NULL);
      ret_code = http_send_file_header(conn, filename, HTTP_NOT_FOUND);
    }
    /* Can't find the 404 page: This likely means there is no file system */
    else
    {
      //fprintf(stderr, "Can't open the Not found file.\n");
      //fprintf(stderr, "Have you programmed the filing system into flash?\n");
      send(conn->fd,(void*)canned_http_response,strlen(canned_http_response),0);
      
      hyclose(conn->file_handle);
      conn->state = RESET;
      return -1;  
    }
  }
  /* We've found the requested file; send its header and move on. */  
  else
  {
    ret_code = http_send_file_header(conn, filename, HTTP_OK);
  }

  return ret_code;
}

/*
 * http_handle_post()
 *
 * Process the post request and take the appropriate action.
 */
int http_handle_post(http_conn* conn)
{
  alt_u8* tx_wr_pos = conn->tx_buffer;
  int ret_code = 0;

  tx_wr_pos += sprintf(tx_wr_pos, HTTP_VERSION_STRING);
  tx_wr_pos += sprintf(tx_wr_pos, HTTP_NO_CONTENT_STRING);

  conn->close = 1;

  tx_wr_pos += sprintf(tx_wr_pos, HTTP_CLOSE);

  tx_wr_pos += sprintf(tx_wr_pos, HTTP_END_OF_HEADERS);

  send(conn->fd, conn->tx_buffer, (tx_wr_pos - conn->tx_buffer), 0);  

  if (!strcmp(conn->uri, mapping.name))
  {
    mapping.func();
  }
  
  return ret_code;
}


/*
 * http_prepare_response()
 * 
 * Service the various HTTP commands, calling the relevant subroutine. 
 * We only handle GET and POST.
 */
int http_prepare_response(http_conn* conn)
{
  int ret_code = 0;
  
  switch (conn->action)
  {
    case GET:
    {
      /* Find file from uri */
      ret_code = http_find_file(conn);
      break;
    }
    case POST:
    {
      /* Find file from uri */
      ret_code = http_handle_post(conn);
      break;
    }
    default:
    {
      break;
    }
  } /* switch (conn->action) */
  
  return ret_code;
}

/*
 * http_handle_receive()
 *
 * Work out what the request we received was, and handle it.
 */
void http_handle_receive(http_conn* conn, int http_instance)
{
  int data_used, rx_code;
 
  if (conn->state == READY)
  {
    rx_code = recv(conn->fd, conn->rx_wr_pos, 
              (HTTP_RX_BUF_SIZE - (conn->rx_wr_pos - conn->rx_buffer) -1), 
              MSG_DONTWAIT);
        
    /* 
     * If a valid data received, take care of buffer pointer & string 
     * termination and more on. Otherwise, we need to return and wait for more
     * data to arrive (until we time out).
     */
    if(rx_code > 0)
    {
      conn->rx_wr_pos += rx_code;
      *(conn->rx_wr_pos+1) = 0;
      
      if(strstr(conn->rx_buffer, HTTP_END_OF_HEADERS))
      {
        conn->state = PROCESS;
      }
    }
  }
  
  if(conn->state == PROCESS)
  {
    /* 
     * If we (think) we have valid headers, keep the connection alive a bit
     * longer.
     */
    conn->activity_time = alt_nticks();
    
    /* 
     * Attempt to process the fundamentals of the HTTP request. We may 
     * error out and reset if the request wasn't complete, or something
     * was asked from us that we can't handle.
     */
    if (http_process_request(conn))
    {
      //fprintf(stderr, "[http_handle_receive] http_process_request failed\n");
      conn->state = RESET;
      http_manage_connection(conn, http_instance);
    }
    
    /* 
     * Step through the headers to see if there is any other useful 
     * information about our pending transaction to extract. After that's 
     * done, send some headers of our own back to let the client know 
     * what's happening. Also, once all in-coming headers have been parsed
     * we can manage our RX buffer to prepare for the next in-coming 
     * connection.
     */
    while(conn->state == PROCESS)
    {
      if(http_read_line(conn))
      {
        //fprintf(stderr, "[http_handle_receive] error reading headers\n");
        conn->state = RESET;
        http_manage_connection(conn, http_instance);
        break;
      }
      if(http_process_headers(conn))
      {
        if( (conn->rx_rd_pos = strstr(conn->rx_rd_pos, HTTP_CR_LF)) )
        {
          conn->rx_rd_pos += 2;
          conn->state = DATA;
          conn->activity_time = alt_nticks();
        }
        else
        {
          //fprintf(stderr, "[http_handle_receive] Can't find end of headers!\n");
          conn->state = RESET;
          http_manage_connection(conn, http_instance);
          break;
        }
      } 
    } /* while(conn->state == PROCESS) */
    
    if( http_prepare_response(conn) )
    {
      conn->state = RESET;
      //fprintf(stderr, "[http_handle_receive] Error preparing response\n");
      http_manage_connection(conn, http_instance);
    }
              
    /* 
     * Manage RX Buffer: Slide any un-read data in our input buffer 
     * down over previously-read data that can now be overwritten, and 
     * zero-out any bytes in question at the top of our new un-read space. 
     */
    if(conn->rx_rd_pos > (conn->rx_buffer + HTTP_RX_BUF_SIZE))
    {
      conn->rx_rd_pos = conn->rx_buffer + HTTP_RX_BUF_SIZE;
    }
        
    data_used = conn->rx_rd_pos - conn->rx_buffer;
    memmove(conn->rx_buffer,conn->rx_rd_pos,conn->rx_wr_pos-conn->rx_rd_pos);
    conn->rx_rd_pos = conn->rx_buffer;
    conn->rx_wr_pos -= data_used;
    memset(conn->rx_wr_pos, 0, data_used);
   } 
}

/* 
 * http_handle_transmit()
 * 
 * Transmit a chunk of a file in an active HTTP connection. This routine
 * will be called from the thread's main loop when ever the socket is in 
 * the 'DATA' state and the socket is marked as available for writing (free
 * buffer space). 
 */
void http_handle_transmit(http_conn* conn, int http_instance)
{
  if( http_send_file_chunk(conn) )
  {
    //fprintf(stderr, "[http_handle_transmit]: Send file chunk failed\n");
  }
}

/*
 * http_task()
 * 
 * This MicroC/OS-II thread spins forever after first establishing a listening
 * socket for HTTP connections, binding them, and listening. Once setup,
 * it perpetually waits for incoming data to either a listening socket, or
 * (if there is an active connection), an HTTP data socket. When data arrives, 
 * the approrpriate routine is called to either accept/reject a connection 
 * request, or process incoming data.
 * 
 * This routine calls "select()" to determine which sockets are ready for
 * reading or writing. This, in conjunction with the use of non-blocking 
 * send() and recv() calls and sending responses broken up into chunks lets
 * us handle multiple active HTTP requests.
 */
void http_task()
{
  int     i, fd_listen, max_socket;
  struct  sockaddr_in addr;
  struct  timeval select_timeout;
  fd_set  readfds, writefds;
  static  http_conn     conn[HTTP_NUM_CONNECTIONS];

  /*
   * Sockets primer...
   * The socket() call creates an endpoint for TCP of UDP communication. It 
   * returns a descriptor (similar to a file descriptor) that we call fd_listen,
   * or, "the socket we're listening on for connection requests" in our web
   * server example.
   */ 
  if ((fd_listen = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  {
    die_with_error("[http_task] Listening socket creation failed");
  }
  
  /*
   * Sockets primer, continued...
   * Calling bind() associates a socket created with socket() to a particular IP
   * port and incoming address. In this case we're binding to HTTP_PORT and to
   * INADDR_ANY address (allowing anyone to connect to us. Bind may fail for 
   * various reasons, but the most common is that some other socket is bound to
   * the port we're requesting. 
   */ 
  addr.sin_family = AF_INET;
  addr.sin_port = htons(HTTP_PORT);
  addr.sin_addr.s_addr = INADDR_ANY;
    
  if ((bind(fd_listen,(struct sockaddr *)&addr,sizeof(addr))) < 0)
  {
    die_with_error("[http_task] Bind failed");
  }
    
  /*
   * Sockets primer, continued...
   * The listen socket is a socket which is waiting for incoming connections.
   * This call to listen will block (i.e. not return) until someone tries to 
   * connect to this port.
   */ 
  if ((listen(fd_listen,1)) < 0)
  {
    die_with_error("[http_task] Listen failed");
  }

  /* 
   * At this point we have successfully created a socket which is listening
   * on HTTP_PORT for connection requests from any remote address.
   */
  for(i=0; i<HTTP_NUM_CONNECTIONS; i++)
  {
    http_reset_connection(&conn[i], i);
  }
   
  while(1)
  {
    /* 
     * The select() call below tells the LWIP stack to return  from this call 
     * when any of the events we have expressed an interest in happen (it 
     * blocks until our call to select() is satisfied). 
     * 
     * In the call below we're only interested in either someone trying to 
     * connect to us, or when an existing (active) connection has new receive 
     * data, or when an existing connection is in the "DATA" state meaning that
     * we're in the middle of processing an HTTP request. If none of these 
     * conditions are satisfied, select() blocks until a timeout specified 
     * in the select_timeout struct.
     * 
     * The sockets we're interested in (for RX) are passed in inside the 
     * readfds parameter, while those we're interested in for TX as passed in 
     * inside the writefds parameter. The format of readfds and writefds is 
     * implementation dependant, hence there are standard macros for 
     * setting/reading the values:
     * 
     *   FD_ZERO  - Zero's out the sockets we're interested in
     *   FD_SET   - Adds a socket to those we're interested in
     *   FD_ISSET - Tests whether the chosen socket is set 
     */
    FD_ZERO(&readfds);
    FD_ZERO(&writefds);
    FD_SET(fd_listen, &readfds);
    
    max_socket = fd_listen+1;
    
    for(i=0; i<HTTP_NUM_CONNECTIONS; i++)
    {
      if (conn[i].fd != -1)
      {
        /* We're interested in reading any of our active sockets */
        FD_SET(conn[i].fd, &readfds);
        
        /* 
         * We're interested in writing to any of our active sockets in the DATA
         * state
         */
        if(conn[i].state == DATA)
        {
          FD_SET(conn[i].fd, &writefds);
        }
        
        /*
         * select() must be called with the maximum number of sockets to look 
         * through. This will be the largest socket number + 1 (since we start
         * at zero).
         */
        if (max_socket <= conn[i].fd)
        {
          max_socket = conn[i].fd+1;
        }
      }
    }

    /* 
     * Set timeout value for select. This must be reset for each select()
     * call.
     */
    select_timeout.tv_sec = 0;
    select_timeout.tv_usec = 500000;

    select(max_socket, &readfds, &writefds, NULL, &select_timeout);

    /* 
     * If fd_listen (the listening socket we originally created in this thread
     * is "set" in readfds, then we have an incoming connection request. 
     * We'll call a routine to explicitly accept or deny the incoming connection 
     * request.
     */
    if (FD_ISSET(fd_listen, &readfds))
    {
      http_handle_accept(fd_listen, conn);
    }

    /*
     * If http_handle_accept() accepts the connection, it creates *another*
     * socket for sending/receiving data. This socket is independant of the 
     * listening socket we created above. This socket's descriptor is stored 
     * in conn[i].fd. Therefore if conn[i].fd is set in readfs, we have
     * incoming data for our HTTP server, and we call our receive routine
     * to process it. Likewise, if conn[i].fd is set in writefds, we have
     * an open connection that is *capable* of being written to.
     */
    for(i=0; i<HTTP_NUM_CONNECTIONS; i++)
    {
      if (conn[i].fd != -1)
      { 
        if(FD_ISSET(conn[i].fd,&readfds))
        {
          http_handle_receive(&conn[i], i);
        }
        
        if(FD_ISSET(conn[i].fd,&writefds))
        {
          http_handle_transmit(&conn[i], i);
        }
        
        http_manage_connection(&conn[i], i);
      }
    }  
  } /* while(1) */
}

/******************************************************************************
*                                                                             *
* License Agreement                                                           *
*                                                                             *
* Copyright (c) 2004 Altera Corporation, San Jose, California, USA.           *
* All rights reserved.                                                        *
*                                                                             *
* Permission is hereby granted, free of charge, to any person obtaining a     *
* copy of this software and associated documentation files (the "Software"),  *
* to deal in the Software without restriction, including without limitation   *
* the rights to use, copy, modify, merge, publish, distribute, sublicense,    *
* and/or sell copies of the Software, and to permit persons to whom the       *
* Software is furnished to do so, subject to the following conditions:        *
*                                                                             *
* The above copyright notice and this permission notice shall be included in  *
* all copies or substantial portions of the Software.                         *
*                                                                             *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,    *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE *
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER      *
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING     *
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER         *
* DEALINGS IN THE SOFTWARE.                                                   *
*                                                                             *
* This agreement shall be governed in all respects by the laws of the State   *
* of California and by the laws of the United States of America.              *
* Altera does not recommend, suggest or require that this reference design    *
* file be used in conjunction or combination with any other product.          *
******************************************************************************/
