static int httpd_thread_cleanup(void)
{
  int status = kNoErr;
  
  switch (httpd_state) {
  case HTTPD_INIT_DONE:
    /*
    * We have no threads, no sockets to close.
    */
    break;
  case HTTPD_THREAD_RUNNING:
    status = httpd_signal_and_wait_for_halt();
    if (status != kNoErr)
      httpd_d("Unable to stop thread. Force killing it.");
    /* No break here on purpose */
  case HTTPD_THREAD_SUSPENDED:
    status = mico_rtos_delete_thread(&httpd_main_thread);
    if (status != kNoErr)
      httpd_d("Failed to delete thread.");
    status = httpd_close_sockets();
    httpd_state = HTTPD_INIT_DONE;
    break;
  default:
    return -kInProgressErr;
  }
  
  return status;
}
static int httpd_signal_and_wait_for_halt()
{
  const int total_wait_time_ms = 1000 * 20;	/* 20 seconds */
  const int check_interval_ms = 100;	/* 100 ms */
  
  int num_iterations = total_wait_time_ms / check_interval_ms;
  
  httpd_d("Sent stop request");
  httpd_stop_req = TRUE;
  
  /* Do a dummy local connect to wakeup the httpd thread */
  int sockfd;
  int rv = tcp_local_connect(&sockfd);
  if (rv != kNoErr)
    return rv;
  
  while (httpd_state != HTTPD_THREAD_SUSPENDED && num_iterations--) {
    mico_thread_msleep(check_interval_ms);
  }
  
  close(sockfd);
  if (httpd_state == HTTPD_THREAD_SUSPENDED)
    return kNoErr;
  
  httpd_d("Timed out waiting for httpd to stop. " "Force closed temporary socket");
  
  httpd_stop_req = FALSE;
  return -kInProgressErr;
}
/* This pairs with httpd_shutdown() */
int httpd_init()
{
  int status;
  
  if (httpd_state != HTTPD_INACTIVE)
    return kNoErr;
  
  httpd_d("Initializing");
  
  client_sockfd = -1;
  http_sockfd  = -1;
  
  status = httpd_wsgi_init();
  if (status != kNoErr) {
    httpd_d("Failed to initialize WSGI!");
    return status;
  }
  
  status = httpd_ssi_init();
  if (status != kNoErr) {
    httpd_d("Failed to initialize SSI!");
    return status;
  }
  
  httpd_state = HTTPD_INIT_DONE;
  
  return kNoErr;
}
static void httpd_suspend_thread(bool warn)
{
  if (warn) {
    httpd_d("Suspending thread");
  } else {
    httpd_d("Suspending thread");
  }
  httpd_close_sockets();
  httpd_state = HTTPD_THREAD_SUSPENDED;
  mico_rtos_suspend_thread(NULL);
}
/* This pairs with httpd_init() */
int httpd_shutdown(void)
{
  int ret;
  
  httpd_d("Shutting down.");
  
  ret = httpd_thread_cleanup();
  if (ret != kNoErr)
    httpd_d("Thread cleanup failed");
  
  httpd_state = HTTPD_INACTIVE;
  
  return ret;
}
static void httpd_main(void *arg)
{
  int status, max_sockfd = -1;
  fd_set readfds, active_readfds;
  
  status = httpd_setup_main_sockets();
  if (status != kNoErr)
    httpd_suspend_thread(true);
  
  FD_ZERO(&readfds);
  
  FD_SET(http_sockfd, &readfds);
  max_sockfd = http_sockfd;
  
  while (1) {
    httpd_d("Waiting on main socket");
    httpd_select(max_sockfd, &readfds, &active_readfds, -1);
    httpd_handle_client_connection(&active_readfds);
  }
  
  /*
  * Thread will never come here. The functions called from the above
  * infinite loop will cleanly shutdown this thread when situation
  * demands so.
  */
}
Beispiel #7
0
/* Register an SSI handler */
int httpd_register_ssi(struct httpd_ssi_call *ssi_call)
{
	int i;

	if (!ssi_ready)
		return -kInProgressErr;

	/* Verify that the ssi is not already registered */
	for (i = 0; i < MAX_REGISTERED_SSIS; i++) {
		if (!calls[i]) {
			continue;
		}
		if (strncmp(calls[i]->name, ssi_call->name,
			    strlen(calls[i]->name)) == 0) {
			return -kInProgressErr;
		}
	}

	/* Find an unoccupied slot in the table */
	for (i = 0; i < MAX_REGISTERED_SSIS && calls[i]; i++)
			;

	/* Check that we're not overrunning the table */
	if (i == MAX_REGISTERED_SSIS)
		return -WM_E_HTTPD_SSI_MAX;

	calls[i] = ssi_call;
	httpd_d("Register ssi %s at %d", ssi_call->name, i);

	return kNoErr;
}
Beispiel #8
0
/* Parse the individual components of the HTTP header and reflect it in
* httpd_request_t structure. */
static int __httpd_parse_hdr_tags(char *data_p, int len,
				  httpd_request_t *req_p, uint8_t *done)
{
  //	ASSERT((data_p != NULL) && (req_p != NULL));
  
  if ((*data_p == ISO_nl) || (*data_p == ISO_cr) || (*data_p == 0)) {
    /* Indicate that the parsing is now complete */
    *done = 1;
    return kNoErr;
  }
  
  if (strncasecmp(data_p, http_content_len, sizeof(http_content_len) - 1) == 0) {
    req_p->body_nbytes =
      atol(&data_p[sizeof(http_content_len) - 1]);
    if (req_p->body_nbytes == 0) {
      httpd_d("got 0 for body length");
      return kNoErr;
    }
    req_p->remaining_bytes = req_p->body_nbytes;
  } else if (strncasecmp(data_p, http_user_agent, sizeof(http_user_agent) - 1) == 0) {
    /* chomp the : and ' ' and pass it to the internal parser */
    httpd_parse_useragent(data_p + sizeof(http_user_agent) + 1,
                          &req_p->agent);
  } else if (strncasecmp(data_p, http_content_type, sizeof(http_content_type) - 1) == 0) {
    memcpy(req_p->content_type, data_p + strlen(http_content_type),
           strlen(data_p));
  } else if (strncasecmp(data_p, "If-None-Match", sizeof("If-None-Match") - 1) == 0) {
    /*
    * We use the FTFS CRC to generate ETag. Hence, the ETag we
    * receive is not expected to be more that 32 bits in value.
    */
    const char *first_double_quote = strchr(data_p, '"');
    if (!first_double_quote) {
      httpd_d("If_None_Match has no double quote");
      return -kInProgressErr;
    }
    
    const char *etag_start = ++first_double_quote;
    req_p->etag_val = strtol(etag_start, NULL, 16);
    req_p->if_none_match = TRUE;
  } else if (strncasecmp(data_p, http_encoding, sizeof(http_encoding) - 1) == 0) {
    if (!strncasecmp(&data_p[sizeof(http_encoding) - 1],
                     HTTP_CHUNKED, sizeof(HTTP_CHUNKED) - 1))
      req_p->chunked = 1;
  }
  return kNoErr;
}
static inline int tcp_local_connect(int *sockfd)
{
  uint16_t port;
  int retry_cnt = 3;
  
  httpd_d("Doing local connect for shutting down server\n\r");
  
  *sockfd = -1;
  while (retry_cnt--) {
    *sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (*sockfd >= 0)
      break;
    /* Wait some time to allow some sockets to get released */
    mico_thread_msleep(1000);
  }
  
  if (*sockfd < 0) {
    httpd_d("Unable to create socket to stop server");
    return -kInProgressErr;
  }
  
  port = HTTP_PORT;
  
  char *host = "127.0.0.1";
  struct sockaddr_t addr;
  memset(&addr, 0, sizeof(struct sockaddr_t));
  
  addr.s_port = port;
  addr.s_ip = inet_addr(host);
  
  httpd_d("local connecting ...");
  if (connect(*sockfd, &addr, sizeof(addr)) != 0) {
    httpd_d("Server close error. tcp connect failed %s:%d", host, port);
    close(*sockfd);
    *sockfd = 0;
    return -kInProgressErr;
  }
  
  /*
  * We do not wish to do anything with this connection. Its sole
  * purpose was to wake the main httpd thread out of sleep.
  */
  
  return kNoErr;
}
void httpd_set_error(const char *fmt, ...)
{
	va_list argp;

	va_start(argp, fmt);
	vsnprintf(httpd_error, HTTPD_MAX_ERROR_STRING + 1, fmt, argp);
	va_end(argp);
	httpd_d("%s", httpd_error);
}
static int httpd_send_file(struct fs *fs, int conn,
			   const sys_file_t *sys_file_p)
{
	char data[SND_BUF_LEN];
	int len;
	int total = 0;
	int err = WM_SUCCESS;

	/* Chunk begin */
	err = httpd_send_chunk_begin(conn,
		     htsys_get_file_size(fs, sys_file_p));
	if (err != WM_SUCCESS)
		return err;

	/* Send the file */
	while (TRUE) {
		len = htsys_read(fs, sys_file_p,
				 (unsigned char *)data, SND_BUF_LEN);
		if (len <= 0) {
			if (len < 0)
				httpd_d("read failed\r\n");
			break;
		}
		err = httpd_send(conn, data, len);
		if (err != WM_SUCCESS) {
			httpd_d("Failed to send file (sent %d bytes)\r\n",
				total);
			break;
		}
		total += len;
	}

	htsys_close(fs, sys_file_p);

	if (err != WM_SUCCESS) {
		return err;
	}

	/* Chunk end */
	err = httpd_send_crlf(conn);

	return err;
}
/* This pairs with httpd_stop() */
int httpd_start(void)
{
  int status;
  
  if (httpd_state != HTTPD_INIT_DONE) {
    httpd_d("Already started");
    return kNoErr;
  }
  
  status = mico_rtos_create_thread(&httpd_main_thread, MICO_APPLICATION_PRIORITY, "httpd", 
                                   httpd_main, http_server_thread_stack_size,  NULL);

  if (status != kNoErr) {
    httpd_d("Failed to create httpd thread: %d", status);
    return -kInProgressErr;
  }
  
  httpd_state = HTTPD_THREAD_RUNNING;
  return kNoErr;
}
Beispiel #13
0
int htsys_getln_soc(int sd, char *data_p, int buflen)
{
	int len = 0;
	char *c_p;
	int result;

//	ASSERT(data_p != NULL);
	c_p = data_p;

	/* Read one byte at a time */
	while ((result = httpd_recv(sd, c_p, 1, 0)) != 0) {
		/* error on recv */
		if (result == -1) {
			*c_p = 0;
			httpd_d("recv failed len: %d, err: 0x%x", len, serr);
			return -kInProgressErr;
		}

		/* If new line... */
		if ((*c_p == ISO_nl) || (*c_p == ISO_cr)) {
			result = httpd_recv(sd, c_p, 1, 0);
			if ((*c_p != ISO_nl) && (*c_p != ISO_cr)) {
				httpd_d("should get double CR LF: %d, %d",
				      (int)*c_p, result);
			}
			break;
		}
		len++;
		c_p++;

		/* give up here since we'll at least need 3 more chars to
		 * finish off the line */
		if (len >= buflen - 1) {
			httpd_d("buf full: recv didn't read complete line.");
			break;
		}
	}

	*c_p = 0;
	return len;
}
Beispiel #14
0
/* Find a matching SSI function from the table and return it */
httpd_ssifunction httpd_ssi(char *name)
{
	struct httpd_ssi_call *f;
	int i;

	if (!ssi_ready)
		return nullfunction;

	/* Find the matching name in the table, return the function. */
	for (i = 0; i < MAX_REGISTERED_SSIS; i++) {
		f = calls[i];
		if (f && strncmp(f->name, name, strlen(f->name)) == 0) {
			httpd_d("Found function: %s", (f)->name);
			return f->function;
		}
	}

	httpd_d("Did not find function %s returning nullfunc", name);

	return nullfunction;
}
static int httpd_select(int max_sock, const fd_set *readfds,
                        fd_set *active_readfds, int timeout_secs)
{
  int activefds_cnt;
  struct timeval_t timeout;
  
  fd_set local_readfds;
  
  if (timeout_secs >= 0)
    timeout.tv_sec = timeout_secs;
  timeout.tv_usec = 0;
  
  memcpy(&local_readfds, readfds, sizeof(fd_set));
  httpd_d("WAITING for activity");
  
  activefds_cnt = select(1, &local_readfds, NULL, NULL, timeout_secs >= 0 ? &timeout : NULL);
  if (activefds_cnt < 0) {
    httpd_d("Select failed: %d", timeout_secs);
    httpd_suspend_thread(true);
  }
  
  if (httpd_stop_req) {
    httpd_d("HTTPD stop request received");
    httpd_stop_req = FALSE;
    httpd_suspend_thread(false);
  }
  
  if (activefds_cnt) {
    /* Update users copy of fd_set only if he wants */
    if (active_readfds)
      memcpy(active_readfds, &local_readfds, sizeof(fd_set));
    return activefds_cnt;
  }
  
  httpd_d("TIMEOUT");
  
  return HTTPD_TIMEOUT_EVENT;
}
static int httpd_setup_new_socket(int port)
{
  int one = 1;
  int status, sockfd;
  struct sockaddr_t addr_listen;
  
  /* create listening TCP socket */
  sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sockfd < 0) {
    status = net_get_sock_error(sockfd);
    httpd_d("Socket creation failed: Port: %d Status: %d", port, status);
    return status;
  }
  
  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));

  addr_listen.s_ip = INADDR_ANY;
  addr_listen.s_port = port;
  
  /* bind insocket */
  status = bind(sockfd, &addr_listen, sizeof(addr_listen));
  if (status < 0) {
    status = net_get_sock_error(sockfd);
    httpd_d("Failed to bind socket on port: %d Status: %d", status, port);
    return status;
  }
  
  status = listen(sockfd, HTTPD_MAX_BACKLOG_CONN);
  if (status < 0) {
    status = net_get_sock_error(sockfd);
    httpd_d("Failed to listen on port %d: %d.", port, status);
    return status;
  }
  
  httpd_d("Listening on port %d.", port);
  return sockfd;
}
static int httpd_close_sockets()
{
  int ret, status = kNoErr;
  
  if (http_sockfd != -1) {
    ret = close(http_sockfd);
    if (ret != 0) {
      httpd_d("failed to close http socket: %d", net_get_sock_error(http_sockfd));
      status = -kInProgressErr;
    }
  http_sockfd = -1;
  }
  
  if (client_sockfd != -1) {
    ret = close(client_sockfd);
    if (ret != 0) {
      httpd_d("Failed to close client socket: %d", net_get_sock_error(client_sockfd));
      status = -kInProgressErr;
    }
    client_sockfd = -1;
  }
  
  return status;
}
Beispiel #18
0
/* One-by-one read lines terminated by CR-LF from the socket, and
* parse the headers contained in them. */
int httpd_parse_hdr_tags(httpd_request_t *req, int sock, char *buffer, int len)
{
  int req_line_len;
  int err;
  uint8_t done = 0;
  
  while (TRUE) {
    req_line_len = htsys_getln_soc(sock, buffer, len);
    if (req_line_len == -kInProgressErr) {
      httpd_d("Could not read line from socket");
      return -kInProgressErr;
    }
    
    err = __httpd_parse_hdr_tags(buffer, req_line_len, req, &done);
    if (err != kNoErr)
      return err;
    if (done == 1) {
      if (req->agent.product[0] != 0) {
        /* Populate the diagnostics statistics variable
        * with the user agent string.  Do not do
        * anything if the agent string is NULL
        * (http-client). This is required since this
        * code is used by http server as well as client
        * and we do not want the user agent diagnostics
        * variable to change during the client code
        * path
        */
        /* Note that, currently wm_hd_useragent is
        * populated only for file handling requests and
        * HTTP POST requests and not for HTTP GET. This
        * is because, in GET requests we directly purge
        * all the headers without reading them and
        * hence do not populate the useragent */
        //				memcpy(&g_wm_stats.wm_hd_useragent, &req->agent,
        //				       sizeof(g_wm_stats.wm_hd_useragent));
        //				g_wm_stats.wm_hd_time = wmtime_time_get_posix();
      }
      return kNoErr;
    }
  }
}
Beispiel #19
0
/* Unregister an SSI handler */
void httpd_unregister_ssi(struct httpd_ssi_call *ssi_call)
{
	int i;

	if (!ssi_ready)
		return;

	for (i = 0; i < MAX_REGISTERED_SSIS; i++) {
		if (!calls[i]) {
			continue;
		}
		if (strncmp(calls[i]->name, ssi_call->name,
			    strlen(calls[i]->name)) == 0) {
			calls[i] = NULL;
			httpd_d("Unregister ssi %s at %d", ssi_call->name, i);
			return;
		}
	}

	return;
}
Beispiel #20
0
/* Parse the main header (GET /index.html HTTP/1.1) within the HTTP header and
* update the httpd_request structure with the same.
*/
int httpd_parse_hdr_main(const char *data_p, httpd_request_t * req_p)
{
  const char *ptr, *next;
  int err;
#define TOKEN_LEN 10
  char token[TOKEN_LEN];
  
  /* First token is usually GET/POST */
  err = httpd_verify_req_type(data_p, &req_p->type);
  if (err != kNoErr) {
    httpd_d("Unknown request type %s", data_p);
    httpd_purge_headers(req_p->sock);
    return err;
  }
  
  /* Second token: Filename */
  ptr = httpd_next_token(data_p);
  next = ptr;
  
  err = httpd_token_cpy(ptr, req_p->filename, HTTPD_MAX_URI_LENGTH);
  if (err != kNoErr) {
    httpd_set_error("Error processing token: filename. "
                    "Filename missing or too long?");
    return -WM_E_HTTPD_HDR_FNAME;
  }
  
  /* Third token: HTTP version */
  ptr = httpd_next_token(next);
  next = ptr;
  
  err = httpd_token_cpy(ptr, token, TOKEN_LEN);
  if (!strncmp(token, http_10, TOKEN_LEN)) {
    httpd_set_error("HTTP/1.0 clients are not supported");
    return -WM_E_HTTPD_NOTSUPP;
  }
  
  return kNoErr;
}
Beispiel #21
0
static int __httpd_parse_all_tags(char *inbuf, const char *tag,
				  char *val, unsigned val_len)

{
  short found = 0;
  char *ptr;
  
  ptr = inbuf;
  while (1) {
    ptr = httpd_parse_msgbody(ptr, tag, val, val_len, &found);
    if (ptr < 0) {
      httpd_d("Failed to parse request");
      return -kInProgressErr;
    }
    if (found)
      return kNoErr;
    
    if (ptr == NULL)
      break;
  }
  
  return -kInProgressErr;
}
Beispiel #22
0
/* Parse a tag value pair. tag1=value1&tag2=value2
*/
static char *httpd_parse_msgbody(char *data_p, const char *tag,
                                 char *val, unsigned val_len, short *found)
{
  int i, c1, c2;
  char *ptr;
  const char *tag_p;
  
  *found = 0;
  
  if (data_p == NULL || *data_p == '\0')
    return NULL;
  
  ptr = data_p;
  tag_p = tag;
  while (1) {
    if (*ptr == 0) {
      httpd_d("End of string reached");
      return NULL;
    }
    if (*ptr == '=') {
      /* End of tag */
      if (*tag_p != 0)
        goto next_token;
      else
        break;
    }
    /* perform url decode */
    if (*ptr == '%') {
      ptr++;
      c1 = char2nibble(*ptr++);
      c2 = char2nibble(*ptr++);
      if (c1 == -1 || c2 == -1) {
        httpd_d("Invalid URL-encoded"
                " string. Ignoring.");
        return NULL;
      }
      if (*tag_p != ((c1 << 4) | c2))
        goto next_token;
      tag_p++;
    } else if (*ptr == '+') {
      if (*tag_p != ' ')
        goto next_token;
      tag_p++;
      ptr++;
    } else {
      if (*tag_p != *ptr)
        goto next_token;
      ptr++;
      tag_p++;
    }
  }
  
  /* We come here only if tag was found */
  *found = 1;
  ptr = strchr(ptr, '=');	/* skip to next token */
  if (ptr == NULL) {
    httpd_d("No = after tag");
    return NULL;
  }
  ptr++;
  
  for (i = 0; i < val_len; i++) {
    if ((*ptr == '&') || (*ptr == 0)) {
      val[i] = 0;
      break;
    }
    
    /* perform url decode */
    if (*ptr == '%') {
      ptr++;
      c1 = char2nibble(*ptr++);
      c2 = char2nibble(*ptr++);
      if (c1 == -1 || c2 == -1) {
        httpd_d("Invalid URL-encoded"
                " string. Ignoring.");
        return NULL;
      }
      val[i] = (c1 << 4) | c2;
    } else if (*ptr == '+') {
      val[i] = ' ';
      ptr++;
    } else {
      val[i] = *ptr++;
    }
  }
  
  if (i == val_len) {
    val[val_len] = 0;
    if ((*ptr != '&') && (*ptr != 0))
      httpd_d("Max val length exceeded.  "
              "Truncating value ");
  }
  
next_token:
  ptr = strchr(ptr, '&');	/* skip to next token */
  
  if (ptr == NULL) {
    return NULL;
  }
  
  return ptr + 1;
}
static void httpd_handle_client_connection(const fd_set *active_readfds)
{
  int activefds_cnt, status;
  fd_set readfds;
  
  if (httpd_stop_req) {
    httpd_d("HTTPD stop request received");
    httpd_stop_req = FALSE;
    httpd_suspend_thread(false);
  }
  
  status = httpd_accept_client_socket(active_readfds);
  if (status != kNoErr)
    return;
  
  httpd_d("Client socket accepted: %d", client_sockfd);
  FD_ZERO(&readfds);
  FD_SET(client_sockfd, &readfds);
  
  while (1) {
    if (httpd_stop_req) {
      httpd_d("HTTPD stop request received");
      httpd_stop_req = FALSE;
      httpd_suspend_thread(false);
    }
    
    httpd_d("Waiting on client socket");
    activefds_cnt = httpd_select(client_sockfd, &readfds, NULL, HTTPD_CLIENT_SOCK_TIMEOUT);
    
    if (httpd_stop_req) {
      httpd_d("HTTPD stop request received");
      httpd_stop_req = FALSE;
      httpd_suspend_thread(false);
    }
    
    if (activefds_cnt == HTTPD_TIMEOUT_EVENT) {
      /* Timeout has occured */
      httpd_d("Client socket timeout occurred. " "Force closing socket");

      status = close(client_sockfd);
      if (status != kNoErr) {
        status = net_get_sock_error(client_sockfd);
        httpd_d("Failed to close socket %d", status);
        httpd_suspend_thread(true);
      }
      
      client_sockfd = -1;
      break;
    }
    
    httpd_d("Handling %d", client_sockfd);
    /* Note:
    * Connection will be handled with call to
    * httpd_handle_message twice, first for
    * handling request (kNoErr) and second
    * time as there is no more data to receive
    * (client closed connection) and hence
    * will return with status HTTPD_DONE
    * closing socket.
    */
    /* FIXME: remove this memset if all is working well */
    /* memset(&httpd_message_in[0], 0, sizeof(httpd_message_in)); */
    status = httpd_handle_message(client_sockfd);
    if (status == kNoErr) {
      /* The handlers are expected more data on the
      socket */
      continue;
    }
    
    /* Either there was some error or everything went well */
    httpd_d("Close socket %d.  %s: %d", client_sockfd, status == HTTPD_DONE ? "Handler done" : "Handler failed", status);
    
    status = close(client_sockfd);
    if (status != kNoErr) {
      status = net_get_sock_error(client_sockfd);
      httpd_d("Failed to close socket %d", status);
      httpd_suspend_thread(true);
    }
    client_sockfd = -1;
    
    break;
  }
}
static int  httpd_accept_client_socket(const fd_set *active_readfds)
{
  int main_sockfd = -1;
  struct sockaddr_t addr_from;
  int addr_from_len;
  
  if (FD_ISSET(http_sockfd, active_readfds)) {
    main_sockfd = http_sockfd;
    https_active  = FALSE;
  }
  
  addr_from_len = sizeof(addr_from);
  
  client_sockfd = accept(main_sockfd, &addr_from, &addr_from_len);
  if (client_sockfd < 0) {
    httpd_d("net_accept client socket failed %d.", client_sockfd);
    return -kInProgressErr;
  }
  
  /*
  * Enable TCP Keep-alive for accepted client connection
  *  -- By enabling this feature TCP sends probe packet if there is
  *  inactivity over connection for specfied interval
  *  -- If there is no response to probe packet for specified retries
  *  then connection is closed with RST packet to peer end
  *  -- Ref: http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/
  *
  * We are doing this as we have single threaded web server with
  * synchronous (blocking) API usage like send, recv and they might get
  * blocked due to un-availability of peer end, causing web server to
  * be in-responsive forever.
  */
  int optval = true;
  if (setsockopt(client_sockfd, SOL_SOCKET, 0x0008, &optval, sizeof(optval)) == -1) {
    httpd_d("Unsupported option SO_KEEPALIVE: %d", net_get_sock_error(client_sockfd));
  }
  
  /* TCP Keep-alive idle/inactivity timeout is 10 seconds */
  optval = 10;
  if (setsockopt(client_sockfd, IPPROTO_TCP, 0x03, &optval, sizeof(optval)) == -1) {
    httpd_d("Unsupported option TCP_KEEPIDLE: %d", net_get_sock_error(client_sockfd));
  }
  
  /* TCP Keep-alive retry count is 5 */
  optval = 5;
  if (setsockopt(client_sockfd, IPPROTO_TCP, 0x05, &optval, sizeof(optval)) == -1) {
    httpd_d("Unsupported option TCP_KEEPCNT: %d", net_get_sock_error(client_sockfd));
  }
  
  /* TCP Keep-alive retry interval (in case no response for probe
  * packet) is 1 second.
  */
  optval = 1;
  if (setsockopt(client_sockfd, IPPROTO_TCP, 0x04, &optval, sizeof(optval)) == -1) {
    httpd_d("Unsupported option TCP_KEEPINTVL: %d", net_get_sock_error(client_sockfd));
  }
  
  httpd_d("connecting %d to %d.", client_sockfd, addr_from.s_port);
  
  return kNoErr;
}
/* If a script file (shtml), script_file_p is found, send that file to
 * the client over the socket, conn. Process any SSI directives within
 * the file.
 */
static int handle_script(struct fs *fs, httpd_request_t *req, int conn,
		  sys_file_t *script_file_p, char *data_p)
{
	char *cmd, *args;
	int len, err;
	int script_cmd;
	httpd_ssifunction func;
	SYS_FILE_DECL;

	err = WM_SUCCESS;

	/* proccess each line of the script */
	while ((len = htsys_getln(fs, script_file_p, data_p,
				  HTTPD_MAX_MESSAGE + 1)) != 0) {
		if (len < 0) {
			httpd_d("getln failed\r\n");
			err = -WM_FAIL;
			break;
		}

		/* Check if we should start executing a script. */
		script_cmd = httpd_is_script_directive(data_p, len,
						       &cmd, &args);
		if (script_cmd == SSI_INCLUDE_FILE) {
			/* Include another file and send to client */
			if (cmd == NULL)
				continue;

			httpd_d("Opening %s from script", cmd);
			err = htsys_file_open(fs, cmd, sys_filep);
			if (err != WM_SUCCESS) {
				httpd_d("open failed\r\n");
				break;
			}

			httpd_d("sending file %s (%d bytes).\r\n", cmd,
			      htsys_get_file_size(fs, sys_filep));
			err = httpd_send_file(fs, conn, sys_filep);
			if (err != WM_SUCCESS) {
				break;
			}
		} else if (script_cmd == SSI_INCLUDE_VIRTUAL) {
			/* find the function and run it */
			/* it should always at least return a nullfunction */
			if (cmd == NULL)
				continue;
			httpd_d("Invoking SSI %s from script", cmd);
			func = httpd_ssi(cmd);
			/* Pass args in their own buffer so we can use data_p as
			 * scratch */
			if (args == NULL) {
				err = func(req, NULL, conn, data_p);
			} else {
				strncpy(script_args, args, HTTPD_MAX_SSI_ARGS);
				script_args[HTTPD_MAX_SSI_ARGS] = 0;
				err = func(req, script_args, conn, data_p);
			}
		} else {
			/* send embedded html to client */
			err = httpd_send_chunk_begin(conn, len);
			if (err != WM_SUCCESS)
				break;

			err = httpd_send(conn, data_p, len);
			if (err != WM_SUCCESS)
				break;

			err = httpd_send_crlf(conn);
			if (err != WM_SUCCESS)
				break;

		}
	}
	return err;
}
int httpd_handle_file(httpd_request_t *req_p, struct fs *fs)
{
	const char *anchor = "/";
	char tmp[HTTPD_MAX_URI_LENGTH+1];
	const char *encoding = NULL;
	char *msg_in, *ptr, *type = NULL;
	int err, msg_in_len = HTTPD_MAX_MESSAGE - 1, conn = req_p->sock;
	int ret;

	msg_in = os_mem_alloc(HTTPD_MAX_MESSAGE + 2);
	if (!msg_in) {
		httpd_e("Failed to allocate memory for file handling");
		/* Check what needs to be returned */
		return -WM_FAIL;
	}
	memset(msg_in, 0, HTTPD_MAX_MESSAGE + 2);

	SYS_FILE_DECL;

	if (!fs) {
		/* The filesystem wasn't mounted properly */
		httpd_send_hdr_from_str(conn, http_header_404,
					CONTENT_TYPE_PLAIN, encoding,
					NO_CACHE_HEADERS, UNUSED_PARAM);
		httpd_send_default_headers(conn,
				req_p->wsgi->hdr_fields);
		httpd_send_crlf(conn);
		httpd_send(conn, FILE_NOT_FOUND, sizeof(FILE_NOT_FOUND));
		ret = WM_SUCCESS;
		goto out;
	}
	struct ftfs_super *f_super = fs_to_ftfs_sb(fs);

	/* Ensure that the anchor is the first part of the filename */
	ptr = strstr(req_p->filename, anchor);
	if (ptr == NULL ||
	    ptr != req_p->filename) {
		httpd_d("No anchor in filename\r\n");
		ret = httpd_send_error(conn, HTTP_500);
		goto out;
	}

	/* Zap the anchor from the filename */
	ptr = req_p->filename + strlen(anchor);
	err = 0;
	/* anchors are only across directory boundaries */
	if (*ptr != '/')
		req_p->filename[err++] = '/';

	while (*ptr && (*ptr != '?')) {
		req_p->filename[err] = *ptr;
		ptr++;
		err++;
	}
	req_p->filename[err] = '\0';

	/* "/" implies a request for index.html */
	if (strncmp(req_p->filename, "/", 2) == 0) {
		strncpy(req_p->filename, http_index_html,
			sizeof(http_index_html));
	}

	/* Find if .gz version exists for file, if it is then serve that */
	type = strrchr(req_p->filename, ISO_period);
	if (type) {
		strncpy(tmp, req_p->filename, sizeof(tmp));
		strncat(tmp, ".gz", 3);
		httpd_d("Look for gz file if it exists: %s\r\n", tmp);
		if (htsys_file_open(fs, tmp, sys_filep) == WM_SUCCESS) {
			/* Gzipped version exists, serve that */
			httpd_d("Serve gzipped file\r\n");
			strncpy(req_p->filename, tmp, HTTPD_MAX_URI_LENGTH + 1);
			encoding = ".gz";
			htsys_close(fs, sys_filep);
		}
	}

	ret = httpd_parse_hdr_tags(req_p, conn, msg_in, msg_in_len);
	if (ret < 0) {
		httpd_d("Parsing headers failed \r\n");
		ret = httpd_send_error(conn, HTTP_500);
		goto out;
	}

	/* It is not a WSGI, check to see if the file exists */
	if (htsys_file_open(fs, req_p->filename, sys_filep) == -WM_FAIL) {
		httpd_w("file not found: %s\r\n", req_p->filename);

		ret = httpd_send_hdr_from_str(conn, http_header_404,
					      type, encoding, NO_CACHE_HEADERS,
					      UNUSED_PARAM);
		httpd_send_default_headers(conn,
				req_p->wsgi->hdr_fields);
		httpd_send_crlf(conn);
		if (ret != WM_SUCCESS)
			goto out;

		ret = htsys_file_open(fs, http_404_html, sys_filep);
		if (ret == WM_SUCCESS) {
			ret = httpd_send_file(fs, conn, sys_filep);
			goto out;
		} else
			httpd_w("No local 404 file.  Sending empty 404"
			      " response.\r\n");
	} else {
		/* Ok, the file exists, is it a script html or just html */
		g_wm_stats.wm_hd_file++;
		if (req_p->if_none_match &&
		    (f_super->fs_crc32 == req_p->etag_val)) {
			/* We do not need the file handle now */
			htsys_close(fs, sys_filep);

			/* Send Not Modified header */

			/* Send header prologue */
			ret = httpd_send(conn, http_header_304_prologue,
					 strlen(http_header_304_prologue));
			if (ret != WM_SUCCESS)
				goto out;
			httpd_send_header(conn, "Server", "Marvell-WM");
			httpd_send_header(conn, "Connection", "Close");

			/* Send ETag */
			int len;
			const char *h = httpd_get_etag_hdr(req_p->etag_val,
							   &len);
			ret = httpd_send(conn, h, len);
			if (ret != WM_SUCCESS)
				goto out;
			/* Close the header */
			ret = httpd_send_crlf(conn);
			goto out;
		} else  {
			ret = httpd_send_hdr_from_str(conn, http_header_200,
						      type, encoding,
						      SEND_CACHE_HEADERS,
						      f_super->fs_crc32);
			httpd_send_default_headers(conn,
					req_p->wsgi->hdr_fields);
			httpd_send_crlf(conn);

			if (ret != WM_SUCCESS)
				goto out;
			ptr = strchr(req_p->filename, ISO_period);
			if (ptr != NULL && strncmp(ptr, http_shtml,
						   sizeof(http_shtml)
						   - 1) == 0) {
				httpd_d("Handling script: %s", req_p->filename);
				ret = handle_script(fs, req_p, conn,
						    sys_filep, msg_in);
				htsys_close(fs, sys_filep);
				if (ret != WM_SUCCESS) {
					httpd_d("Script failed\r\n");
					goto out;
				}
			} else {
				ret = httpd_send_file(fs, conn, sys_filep);
				if (ret != WM_SUCCESS)
					goto out;
			}
		}
	}
	ret = httpd_send_last_chunk(conn);
out:
	os_mem_free(msg_in);
	return ret;
}
int httpd_use_tls_certificates(const httpd_tls_certs_t *tls_certs)
{

  httpd_d("HTTPS is not enabled in server. ");
  return -kInProgressErr;
}
/* Handle an incoming message (request) from the client. This is the
 * main processing function of the HTTPD.
 */
int httpd_handle_message(int conn)
{
	int err;
	int req_line_len;
	char msg_in[128];

	/* clear out the httpd_req structure */
	memset(&httpd_req, 0x00, sizeof(httpd_req));

	httpd_req.sock = conn;

	/* Read the first line of the HTTP header */
	req_line_len = htsys_getln_soc(conn, msg_in, sizeof(msg_in));
	if (req_line_len == 0)
		return HTTPD_DONE;

	if (req_line_len < 0) {
		httpd_e("Could not read from socket");
		return -WM_FAIL;
	}

	/* Parse the first line of the header */
	err = httpd_parse_hdr_main(msg_in, &httpd_req);
	if (err == -WM_E_HTTPD_NOTSUPP)
		/* Send 505 HTTP Version not supported */
		return httpd_send_error(conn, HTTP_505);
	else if (err != WM_SUCCESS) {
		/* Send 500 Internal Server Error */
		return httpd_send_error(conn, HTTP_500);
	}

	/* set a generic error that can be overridden by the wsgi handling. */
	httpd_d("Presetting error to: ");
	httpd_set_error("wsgi handler failed");

	/* Web Services Gateway Interface branch point:
	 * At this point we have the request type (httpd_req.type) and the path
	 * (httpd_req.filename) and all the headers waiting to be read from
	 * the socket.
	 *
	 * The call bellow will iterate through all the url patterns and
	 * invoke the handlers that match the request type and pattern.  If
	 * request type and url patern match, invoke the handler.
	 */
	err = httpd_wsgi(&httpd_req);

	if (err == HTTPD_DONE) {
		httpd_d("Done processing request.");
		return WM_SUCCESS;
	} else if (err == -WM_E_HTTPD_NO_HANDLER) {
		httpd_w("No handler for the given URL %s was found",
			httpd_req.filename);
		/*
		 * We have not yet read the complete data from the current
		 * request, from the socket. We are in an error state and
		 * we wish to cancel this HTTP transaction. We sent
		 * appropriate message to the client and read (flush) out
		 * all the pending data in the socket. We let the client
		 * close the socket for us, if necessary.
		 */
		httpd_purge_socket_data(&httpd_req, msg_in,
				sizeof(msg_in), conn);
		httpd_set_error("File %s not_found", httpd_req.filename);
		httpd_send_error(conn, HTTP_404);
		return WM_SUCCESS;
	} else {
		httpd_e("WSGI handler failed.");
		/* Send 500 Internal Server Error */
		return httpd_send_error(conn, HTTP_500);
	}

}