httpd.c

来自「NXPl788上lwip的无操作系统移植,基于Embest开发板」· C语言 代码 · 共 1,866 行 · 第 1/5 页

C
1,866
字号
strnstr(const char* buffer, const char* token, size_t n)
{
  const char* p;
  int tokenlen = (int)strlen(token);
  if (tokenlen == 0) {
    return (char *)buffer;
  }
  for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
    if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
      return (char *)p;
    }
  }
  return NULL;
} 
#endif /* LWIP_HTTPD_STRNSTR_PRIVATE */

/** Allocate a struct http_state. */
static struct http_state*
http_state_alloc(void)
{
  struct http_state *ret;
#if HTTPD_USE_MEM_POOL
  ret = (struct http_state *)memp_malloc(MEMP_HTTPD_STATE);
#else /* HTTPD_USE_MEM_POOL */
  ret = (struct http_state *)mem_malloc(sizeof(struct http_state));
#endif /* HTTPD_USE_MEM_POOL */
  if (ret != NULL) {
    /* Initialize the structure. */
    memset(ret, 0, sizeof(struct http_state));
#if LWIP_HTTPD_DYNAMIC_HEADERS
    /* Indicate that the headers are not yet valid */
    ret->hdr_index = NUM_FILE_HDR_STRINGS;
#endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
  }
  return ret;
}

/** Free a struct http_state.
 * Also frees the file data if dynamic.
 */
static void
http_state_free(struct http_state *hs)
{
  if (hs != NULL) {
    if(hs->handle) {
#if LWIP_HTTPD_TIMING
      u32_t ms_needed = sys_now() - hs->time_started;
      u32_t needed = LWIP_MAX(1, (ms_needed/100));
      LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n",
        ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed)));
#endif /* LWIP_HTTPD_TIMING */
      fs_close(hs->handle);
      hs->handle = NULL;
    }
#if LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS
    if (hs->buf != NULL) {
      mem_free(hs->buf);
      hs->buf = NULL;
    }
#endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
#if HTTPD_USE_MEM_POOL
    memp_free(MEMP_HTTPD_STATE, hs);
#else /* HTTPD_USE_MEM_POOL */
    mem_free(hs);
#endif /* HTTPD_USE_MEM_POOL */
  }
}

/** Call tcp_write() in a loop trying smaller and smaller length
 *
 * @param pcb tcp_pcb to send
 * @param ptr Data to send
 * @param length Length of data to send (in/out: on return, contains the
 *        amount of data sent)
 * @param apiflags directly passed to tcp_write
 * @return the return value of tcp_write
 */
static err_t
http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
{
   u16_t len;
   err_t err;
   LWIP_ASSERT("length != NULL", length != NULL);
   len = *length;
   do {
     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len));
     err = tcp_write(pcb, ptr, len, apiflags);
     if (err == ERR_MEM) {
       if ((tcp_sndbuf(pcb) == 0) ||
           (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
         /* no need to try smaller sizes */
         len = 1;
       } else {
         len /= 2;
       }
       LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, 
                   ("Send failed, trying less (%d bytes)\n", len));
     }
   } while ((err == ERR_MEM) && (len > 1));

   if (err == ERR_OK) {
     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len));
   } else {
     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
   }

   *length = len;
   return err;
}

/**
 * The connection shall be actively closed.
 * Reset the sent- and recv-callbacks.
 *
 * @param pcb the tcp pcb to reset callbacks
 * @param hs connection state to free
 */
static err_t
http_close_conn(struct tcp_pcb *pcb, struct http_state *hs)
{
  err_t err;
  LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb));

#if LWIP_HTTPD_SUPPORT_POST
  if (hs != NULL) {
    if ((hs->post_content_len_left != 0)
#if LWIP_HTTPD_POST_MANUAL_WND
       || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0))
#endif /* LWIP_HTTPD_POST_MANUAL_WND */
       ) {
      /* make sure the post code knows that the connection is closed */
      http_post_response_filename[0] = 0;
      httpd_post_finished(hs, http_post_response_filename, LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN);
    }
  }
#endif /* LWIP_HTTPD_SUPPORT_POST*/


  tcp_arg(pcb, NULL);
  tcp_recv(pcb, NULL);
  tcp_err(pcb, NULL);
  tcp_poll(pcb, NULL, 0);
  tcp_sent(pcb, NULL);
  if(hs != NULL) {
    http_state_free(hs);
  }

  err = tcp_close(pcb);
  if (err != ERR_OK) {
    LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb));
    /* error closing, try again later in poll */
    tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
  }
  return err;
}
#if LWIP_HTTPD_CGI
/**
 * Extract URI parameters from the parameter-part of an URI in the form
 * "test.cgi?x=y" @todo: better explanation!
 * Pointers to the parameters are stored in hs->param_vals.
 *
 * @param hs http connection state
 * @param params pointer to the NULL-terminated parameter string from the URI
 * @return number of parameters extracted
 */
static int
extract_uri_parameters(struct http_state *hs, char *params)
{
  char *pair;
  char *equals;
  int loop;

  /* If we have no parameters at all, return immediately. */
  if(!params || (params[0] == '\0')) {
      return(0);
  }

  /* Get a pointer to our first parameter */
  pair = params;

  /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the
   * remainder (if any) */
  for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) {

    /* Save the name of the parameter */
    hs->params[loop] = pair;

    /* Remember the start of this name=value pair */
    equals = pair;

    /* Find the start of the next name=value pair and replace the delimiter
     * with a 0 to terminate the previous pair string. */
    pair = strchr(pair, '&');
    if(pair) {
      *pair = '\0';
      pair++;
    } else {
       /* We didn't find a new parameter so find the end of the URI and
        * replace the space with a '\0' */
        pair = strchr(equals, ' ');
        if(pair) {
            *pair = '\0';
        }

        /* Revert to NULL so that we exit the loop as expected. */
        pair = NULL;
    }

    /* Now find the '=' in the previous pair, replace it with '\0' and save
     * the parameter value string. */
    equals = strchr(equals, '=');
    if(equals) {
      *equals = '\0';
      hs->param_vals[loop] = equals + 1;
    } else {
      hs->param_vals[loop] = NULL;
    }
  }

  return loop;
}
#endif /* LWIP_HTTPD_CGI */

#if LWIP_HTTPD_SSI
/**
 * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file.
 * The tag's name is stored in hs->tag_name (NULL-terminated), the replacement
 * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).
 * The amount of data written is stored to hs->tag_insert_len.
 *
 * @todo: return tag_insert_len - maybe it can be removed from struct http_state?
 *
 * @param hs http connection state
 */
static void
get_tag_insert(struct http_state *hs)
{
  int loop;
  size_t len;
#if LWIP_HTTPD_SSI_MULTIPART
  u16_t current_tag_part = hs->tag_part;
  hs->tag_part = HTTPD_LAST_TAG_PART;
#endif /* LWIP_HTTPD_SSI_MULTIPART */

  if(g_pfnSSIHandler && g_ppcTags && g_iNumTags) {

    /* Find this tag in the list we have been provided. */
    for(loop = 0; loop < g_iNumTags; loop++) {
      if(strcmp(hs->tag_name, g_ppcTags[loop]) == 0) {
        hs->tag_insert_len = g_pfnSSIHandler(loop, hs->tag_insert,
           LWIP_HTTPD_MAX_TAG_INSERT_LEN
#if LWIP_HTTPD_SSI_MULTIPART
           , current_tag_part, &hs->tag_part
#endif /* LWIP_HTTPD_SSI_MULTIPART */
#if LWIP_HTTPD_FILE_STATE
           , hs->handle->state
#endif /* LWIP_HTTPD_FILE_STATE */
           );
        return;
      }
    }
  }

  /* If we drop out, we were asked to serve a page which contains tags that
   * we don't have a handler for. Merely echo back the tags with an error
   * marker. */
#define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG "
#define UNKNOWN_TAG1_LEN  18
#define UNKNOWN_TAG2_TEXT "***</b>"
#define UNKNOWN_TAG2_LEN  7
  len = LWIP_MIN(strlen(hs->tag_name),
    LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN));
  MEMCPY(hs->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
  MEMCPY(&hs->tag_insert[UNKNOWN_TAG1_LEN], hs->tag_name, len);
  MEMCPY(&hs->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
  hs->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;

  len = strlen(hs->tag_insert);
  LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
  hs->tag_insert_len = (u16_t)len;
}
#endif /* LWIP_HTTPD_SSI */

#if LWIP_HTTPD_DYNAMIC_HEADERS
/**
 * Generate the relevant HTTP headers for the given filename and write
 * them into the supplied buffer.
 */
static void
get_http_headers(struct http_state *pState, char *pszURI)
{
  unsigned int iLoop;
  char *pszWork;
  char *pszExt;
  char *pszVars;

  /* Ensure that we initialize the loop counter. */
  iLoop = 0;

  /* In all cases, the second header we send is the server identification
     so set it here. */
  pState->hdrs[1] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];

  /* Is this a normal file or the special case we use to send back the
     default "404: Page not found" response? */
  if (pszURI == NULL) {
    pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
    pState->hdrs[2] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];

    /* Set up to send the first header string. */
    pState->hdr_index = 0;
    pState->hdr_pos = 0;
    return;
  } else {
    /* We are dealing with a particular filename. Look for one other
       special case.  We assume that any filename with "404" in it must be
       indicative of a 404 server error whereas all other files require
       the 200 OK header. */
    if (strstr(pszURI, "404")) {
      pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
    } else if (strstr(pszURI, "400")) {
      pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST];
    } else if (strstr(pszURI, "501")) {
      pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL];
    } else {
      pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
    }

    /* Determine if the URI has any variables and, if so, temporarily remove 
       them. */
    pszVars = strchr(pszURI, '?');
    if(pszVars) {
      *pszVars = '\0';
    }

    /* Get a pointer to the file extension.  We find this by looking for the
       last occurrence of "." in the filename passed. */
    pszExt = NULL;
    pszWork = strchr(pszURI, '.');
    while(pszWork) {
      pszExt = pszWork + 1;
      pszWork = strchr(pszExt, '.');
    }

    /* Now determine the content type and add the relevant header for that. */
    for(iLoop = 0; (iLoop < NUM_HTTP_HEADERS) && pszExt; iLoop++) {
      /* Have we found a matching extension? */
      if(!strcmp(g_psHTTPHeaders[iLoop].extension, pszExt)) {
        pState->hdrs[2] =
          g_psHTTPHeaderStrings[g_psHTTPHeaders[iLoop].headerIndex];
        break;
      }
    }

    /* Reinstate the parameter marker if there was one in the original URI. */
    if(pszVars) {
      *pszVars = '?';
    }
  }

  /* Does the URL passed have any file extension?  If not, we assume it
     is a special-case URL used for control state notification and we do
     not send any HTTP headers with the response. */
  if(!pszExt) {
    /* Force the header index to a value indicating that all headers
       have already been sent. */
    pState->hdr_index = NUM_FILE_HDR_STRINGS;
  } else {
    /* Did we find a matching extension? */
    if(iLoop == NUM_HTTP_HEADERS) {
      /* No - use the default, plain text file type. */
      pState->hdrs[2] = g_psHTTPHeaderStrings[HTTP_HDR_DEFAULT_TYPE];
    }

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?