Possible approaches
There are basically two different approaches to storing the data:
- You can store a pointer into the original strtok'ed string
- You can strcpy the string from the original strtok'ed string into the array
It depends on the lifetime of the original strtok'ed string - if the data in the array needs to outlive the string then you can't store pointers. But if pointers can be stored then there is a memory savings by not having to allocate memory for the strings twice.
So in my example of how to do it, I will store the pointers. If that's now what you are after you can allocate the array with that additional dimension and use strcpy to save the actual values.
How to understand HTTP messages
In an HTTP message all the lines in the message except the body must end with a carriage return and linefeed pair. See https://stackoverflow.com/a/27966412/2193968 and https://stackoverflow.com/a/5757349/2193968
If you want to hard code the message in your program you might think you can Just type in one long string with embedded end-of-line characters - like this:
char * string = "GET / HTTP/1.1\r\
Host: hostname\r\
\r\
body";
And it might even work - if you are lucky. But how do you know what character that embedded end-of-line character is? It depends on your editor (a Unicode editor will use something different from an ANSI editor) and it depends on your OS (Windows uses something different from Linus). It just isn't good to make that assumption. Besides that it messes up the syntax highlighting and confuses everybody that looks at your code.
But the language has anticipated that and will automatically concatenate consecutive strings:
char * string = "GET / HTTP/1.1\r\n"
"Host: hostname\r\n"
"\r\n"
"body";
So, to find the actual data in the body, the body doesn't need to contain special characters like # because we can just look for the first blank line in the message which is designed to separate the header from the body. The way to recognise that division is to find a carriage return and line feed pair that is immediately followed by another one. It needs to be the first such sequence in the message because the body might contain that character sequence as well.
Now, the body might contain URL encoded form data and this might always be true with the message in your question, but it isn't always true. You need to check the Content-Type header for the actual type of the body. I will assume that doesn't need to be checked in my example but it is something you should watch for. The other important tone is the Content-Length header - it contains the number of bytes of data in the body (not including the header and the blank line) For URL encoded form data like yours it probably doesn't matter but for any other Content-Type it probably does. The correct Content-Type for URL encoded form data is application/x-www-form-urlencoded
See https://stackoverflow.com/a/14551320/2193968
How to parse URL encoded form HTTP bodies
So once we get a string pointer pointing to the body (by looking for the first blank line in the message) we can strtok the string. URL encoded form data has keys and values separated by = and KVPs separated by & and we could just call strtok with a delimiter string of "=&"
except if the value contains an equal sign our determination of whether we are getting a key or value gets out of sync with the data. So it is better if we look for the specific character we expect to find and stay in sync - always knowing where we are.
So in my example I alternate the delimiter based on whether I am storing a key or a value.
The code
You can try it here: https://www.onlinegdb.com/Symtd53LB
Hopefully the comments i nthe code are helpful enough. If not comment and I will add some detail.
#include <stdio.h>
#include <string.h>
int main ()
{
// in valid HTTP, each line of the header and the blank
// line must end with a carraige return and linefeed
char str[] = "HTTP/1.1 200 OK\r\n"
"Date: Sun, 10 Oct 2010 23:26:07 GMT\r\n"
"Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g\r\n"
"Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT\r\n"
"ETag: \"45b6-834-49130cc1182c0\"\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 12\r\n"
"Connection: close\r\n"
"Content-Type: text/html\r\n"
"\r\n"
"var1=red&var2=green&var3=up&var5=down&time=123443291&key=xmskwirrrr3";
// so, we want to find the blank line and then move to the next non-blank line
// no error checking has been done...
char *data = strstr(str,"\r\n\r\n")+strlen("\r\n\r\n");
printf("\nBody = %s\n", data);
// we will use an array of 1000 pairs of pointers
// so we can only handle 1000 KVP max...
char *pointers[1000][2] = {}; // default initialise all of them to MULL
// we need to keep track of the KVP number
// KVP starts at 0 and might go to 999
int KVP = 0;
// and a flag that indicates whether it is the key or value we are parsing
// key when key_value=0, value when key_value=1
#define KVP_KEY 0
#define KVP_VALUE 1
int key_value = KVP_KEY; // the key should be first
// first strtok the first value
// we need to do this because the first time strtok needs different parameters
pointers[KVP][key_value] = strtok(data,"=");
// loop through splitting it up and saving the pointers
// no error checking has been done...
while(KVP < 1000 && pointers[KVP][key_value] != NULL)
{
// ok, we need to update KVP and key_value
if (key_value == KVP_VALUE)
{
KVP++;
key_value = KVP_KEY;
pointers[KVP][key_value] = strtok(NULL,"=");
}
else // if (key_value == KVP_KEY)
{
key_value = KVP_VALUE;
pointers[KVP][key_value] = strtok(NULL,"&");
}
}
// now it should all be parsed
// let's print it all out
for(int i=0; i<KVP; i++)
{
printf("\nkey[%d] = %s\nvalue[%d] = %s\n",
i, pointers[i][KVP_KEY],
i, pointers[i][KVP_VALUE]);
}
return 0;
}