2

I am writing a simple HTTP server and I need to parse the incoming request.

Say I have read a line like the following GET /file HTTP/1.1

I need to split it up into GET /file

I have tried a plethora of methods and I can't seem to get any to work.

The catch is that the server reads the incoming request in chunks inside the following while loop:

char echoBuffer[RCVBUFSIZE];
int recvMsgSize;

if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
        DieWithError("recv() failed");

while (recvMsgSize > 0) {

      if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
            DieWithError("recv() failed");
}

I have tried the following ways inside the loop:

char *token;
for (i = 0; i < 3; i++){
      switch (i) {
        case 0:
          token = strtok(echoBuffer, "/");
          printf("%s\n", token[i]);
        case 1:
          token = strtok(echoBuffer, "HTTP");
          printf("%s\n", token[i]);
      }
    }

But all this prints is GET several times.

I have tried defining a string char *echoString and using strcat()

This just fails all together, and I would expect that since the buffer is a char array.

So what are my options here?

AShelly
  • 34,686
  • 15
  • 91
  • 152
  • 2
    What makes you think `strtok()` can be used on a buffer that was filled by `recv()`? Specifically, how do you ensure null-termination? – EOF Feb 22 '16 at 21:01
  • Would you recommend converting the char array to char* before using any sort of `str()` function? I'm not sure how I can ensure null termination other that it successfully spliced GET –  Feb 22 '16 at 21:04
  • 1
    see http://stackoverflow.com/q/33618837/10396, which despite the bad name, is closely related. You need to split this into two steps: 1) recieving whole requests; 2) Tokenizing the data. – AShelly Feb 22 '16 at 21:06
  • Note that in this context, receiving whole requests means reading until you find `\r\n\r\n` in the input stream. – user3386109 Feb 22 '16 at 21:11
  • Do not use `strtok` for this purpose (or any purpose really), it modifies the buffer and is not re-entrant – M.M Feb 22 '16 at 21:21
  • Your `strtok` usage has a couple problems - please read its documentation more carefully. Also, use `strtok_r` or `strsep`, not `strtok`. You'll thank yourself later if your server ever ends up being multi-threaded. – Brian McFarland Feb 22 '16 at 21:32

1 Answers1

0

As mentioned in above comments, you need to

  • be sure about format of the string returned from recv() call
  • Tokenize the received string

Be aware since any strtok() family function, including my implementation below, relies on static variables, they are not thread safe! Pay extra care here...

#include <stdio.h>
#include <stdlib.h>

/* a dummy recv() implementation that returns the size
* of received buffer */
size_t recv_(int s, void *buf, size_t len,  int flags) {
    len=0;
    char *tmp=buf;

    for (; *tmp; ++tmp, ++len)
        ;

    return len;
}

/* Modified version of zstring_strtok
* first call        -> zstrint_strtok(char *str, char *delim, int len)
* consecutive calls -> zstrint_strtok(NULL, char *delim,0)
*/
char *zstring_strtok(char *str, const char *delim, int len) {
    static char *static_str=0;      /* var to store last address */
    int index=0, strlength=len;     /* integers for indexes */
    int found = 0;                  /* check if delim is found */

    /* delimiter cannot be NULL
    * if no more char left, return NULL as well
    */
    if (delim==0 || (str == 0 && static_str == 0))
        return 0;

    if (str == 0)
        str = static_str;

    /* get length of string */
    if (len==0)
        while(str[strlength])
            strlength++;
    else
        strlength=len;

    /* find the first occurance of delim */
    for (index=0;index<strlength;index++)
        if (str[index]==delim[0]) {
            found=1;
            break;
        }

    /* if delim is not contained in str, return str */
    if (!found) {
        static_str = 0;
        return str;
    }

    /* check for consecutive delimiters
    *if first char is delim, return delim
    */
    if (str[0]==delim[0]) {
        static_str = (str + 1);
        return (char *)delim;
    }

    /* terminate the string
    * this assignmetn requires char[], so str has to
    * be char[] rather than *char
    */
    str[index] = '\0';

    /* save the rest of the string */
    if ((str + index + 1)!=0)
        static_str = (str + index + 1);
    else
        static_str = 0;

        return str;
}


int main()
{
    char response[]="GET /file HTTP/1.1";
    char *buf;
    int recv_msg_size;

    buf=malloc(256);

    if((recv_msg_size=recv_(0,response,0,0)) > 0){
        buf=zstring_strtok(response," ",recv_msg_size);
        do{
            printf("%s\n",buf);
        } while(buf=zstring_strtok(NULL," ",0));
    }


    return 0;
}

The zstring_strtok() function is a modified version of zstring_strtok() in the zString library, a BSD licensed string processing library for C.

the recv_() function above is a dummy replacement of the recv() system call, just to make the exmaple code more readible for you. On my system (FreeBSD 10.2-RELEASE), recv(3) is defined as

 ssize_t
 recv(int s, void *buf, size_t len, int flags);

Output of the above example is

% ./recv
GET
/file
HTTP/1.1
fnisi
  • 1,181
  • 1
  • 14
  • 24