How does a httpPlatformHandler program get posted data?

Roland Smith 1 Reputation point
2022-06-15T01:30:09.54+00:00

I wrote a httpPlatformHandler server app using C++ and Winsock functions. When the URL is called with GET from a browser, recv returns headers. If I call the URL from a program that does a GET and includes data, I see the Content-Length header but no data. If I do a POST, recv returns nothing.

I need to know how my app can access posted data.

Another issue is that when calling from a browser, occasionally recv returns nothing or a bad gateway error.

If Winsock expert would like to look at the code, I can post that.

Internet Information Services
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,431 questions
0 comments No comments
{count} votes

8 answers

Sort by: Most helpful
  1. Bruce (SqlWork.com) 57,241 Reputation points
    2022-06-15T20:55:48.27+00:00

    there is no posted data with a get;

    http requests have 3 parts

    the first line is the method, resouceid, and protocol version:

    this is followed by header lines (terminated by 2 line endings)

    optionally depend on the method, a body is included. the format and length are specified in headers.

    see:

    https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages

    an http request is a tcp/ip stream. a request stream is sent, and a response stream is returned. if keep alive is set, an addition request can be sent after the previous is sent. the stream is typically buffered and a chunked protocol is also available.

    the server knows it reached the end of the message by either the body length is specified or the client sends a fin. typically you would read the headers (just check for the double line ending), and then check the headers to see if a body is included. then read the body.

    0 comments No comments

  2. Roland Smith 1 Reputation point
    2022-06-15T23:16:43.063+00:00

    Technically there can be posted data with a GET, that is just a guideline. I have ISAPI and FastCGI server apps and both of them receive GET data. That is besides the point anyway.

    If I send a GET with data, the Winsock recv function returns headers (including Content-Length) but no data. If I send a POST with data, the Winsock recv function returns nothing.

    The gist of my question is unanswered - how does my server program receive posted data from the httpPlatformHandler module?

    0 comments No comments

  3. Roland Smith 1 Reputation point
    2022-06-15T23:43:15.2+00:00

    Here is the entire program source:

    #include <winsock2.h>  
    #include <ws2tcpip.h>  
    #include <tchar.h>  
    #include <stdio.h>  
    #include <string>  
    using std::string;  
      
    #pragma comment (lib, "Ws2_32.lib")  
      
    #pragma warning (disable: 6387)  
      
    void GetLastError(int iErrorNum);  
    void SendResponse(SOCKET client, LPCSTR lpstrResponse);  
    void SetBlockingMode(SOCKET sock, BOOL blocking);  
    void WriteToLogFile(LPCSTR lpstrMessage);  
    int WebServer(LPTSTR lpstrServiceName);  
      
    int _tmain(int argc, TCHAR* argv[])  
    {  
    	TCHAR szServiceName[MAX_PATH];  
      
    	wmemset(szServiceName, 0x00, _countof(szServiceName));  
      
    	// Get ServiceName (port)  
    	if (argc == 1) {  
    		_tcscpy_s(szServiceName, _countof(szServiceName), L"9000");  
    	}  
    	else {  
    		_tcscpy_s(szServiceName, _countof(szServiceName), argv[1]);  
    	}  
      
    	// Start web server  
    	return WebServer(szServiceName);  
    }  
      
    void GetLastError(int iErrorNum)  
    {  
    	CHAR szBuffer[MAX_PATH];  
      
    	memset(szBuffer, 0x00, sizeof(szBuffer));  
      
    	sprintf_s(szBuffer, sizeof(szBuffer), "ErrorNum: %d", iErrorNum);  
    	WriteToLogFile(szBuffer);  
      
    	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL,  
    		iErrorNum, LANG_NEUTRAL, szBuffer, MAX_PATH, 0);  
      
    	WriteToLogFile(szBuffer);  
      
    	WSASetLastError(0);  
    }  
      
    void SendResponse(SOCKET client, LPCSTR lpstrResponse)  
    {  
    	int length = 0;  
      
    	length = strlen(lpstrResponse);  
      
    	send(client, lpstrResponse, length, 0);  
    }  
      
    void SetBlockingMode(SOCKET sock, BOOL blocking)  
    {  
    	u_long argp;  
      
    	if (blocking) {  
    		argp = 0;  
    	}  
    	else {  
    		argp = 1;  
    	}  
      
    	ioctlsocket(sock, FIONBIO, &argp);  
    }  
      
    void WriteToLogFile(LPCSTR lpstrMessage)  
    {  
    	HANDLE hFile;  
    	DWORD dwWritten = 0;  
      
    	hFile = CreateFileA("C:\\Temp\\LogFile.txt",  
    		FILE_APPEND_DATA, FILE_SHARE_WRITE, NULL,  
    		OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);  
    	if (hFile != INVALID_HANDLE_VALUE) {  
    		WriteFile(hFile, lpstrMessage, (DWORD)strlen(lpstrMessage), &dwWritten, 0);  
    		WriteFile(hFile, "\r\n", 2, &dwWritten, 0);  
    		CloseHandle(hFile);  
    	}  
    }  
      
    int WebServer(LPTSTR lpstrServiceName)  
    {  
    	WSADATA wsaData;  
    	int iResult = 0;  
    	CHAR szRecvBuffer[1024];  
    	string ReceivedData;  
      
    	SYSTEMTIME lt;  
    	char szMsgText[MAX_PATH];  
    	char szTemp[34];  
      
    	SOCKET ListenSocket = INVALID_SOCKET;  
    	SOCKET ClientSocket = INVALID_SOCKET;  
      
    	struct addrinfoW* result = NULL;  
    	struct addrinfoW hints;  
      
    	memset(szMsgText, 0x00, sizeof(szMsgText));  
    	memset(szTemp, 0x00, sizeof(szTemp));  
      
    	// Start Winsock  
    	iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);  
    	if (iResult != 0) {  
    		WriteToLogFile("WSAStartup error: ");  
    		GetLastError(iResult);  
    		return 1;  
    	}  
      
    	// Initialze the structure  
    	ZeroMemory(&hints, sizeof(hints));  
    	hints.ai_family = AF_INET;  
    	hints.ai_socktype = SOCK_STREAM;  
    	hints.ai_protocol = IPPROTO_TCP;  
    	hints.ai_flags = AI_PASSIVE;  
      
    	// Resolve the server address and port  
    	iResult = GetAddrInfoW(NULL, lpstrServiceName, &hints, &result);  
    	if (iResult != 0) {  
    		WriteToLogFile("GetAddrInfoW error: ");  
    		GetLastError(iResult);  
    		WSACleanup();  
    		return 1;  
    	}  
      
    	// Create a SOCKET for connecting to server  
    	ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);  
    	if (ListenSocket == INVALID_SOCKET) {  
    		WriteToLogFile("Socket error: ");  
    		GetLastError(WSAGetLastError());  
    		FreeAddrInfoW(result);  
    		WSACleanup();  
    		return 1;  
    	}  
      
    	// Setup the TCP listening socket  
    	iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);  
    	if (iResult == SOCKET_ERROR) {  
    		WriteToLogFile("Bind error: ");  
    		GetLastError(WSAGetLastError());  
    		FreeAddrInfoW(result);  
    		WSACleanup();  
    		return 1;  
    	}  
      
    	// Cleanup  
    	FreeAddrInfoW(result);  
      
    	// Listen for connections  
    	iResult = listen(ListenSocket, SOMAXCONN);  
    	if (iResult == SOCKET_ERROR) {  
    		WriteToLogFile("Listen error: ");  
    		GetLastError(WSAGetLastError());  
    		closesocket(ListenSocket);  
    		WSACleanup();  
    		return 1;  
    	}  
      
    	// Make the socket blocking  
    	SetBlockingMode(ListenSocket, TRUE);  
      
    	while (0 == 0)  
    	{  
    		// Accept incoming connection  
    		ClientSocket = accept(ListenSocket, NULL, NULL);  
    		if (ClientSocket == INVALID_SOCKET) {  
    			WriteToLogFile("Accept error: ");  
    			GetLastError(WSAGetLastError());  
    			closesocket(ListenSocket);  
    			WSACleanup();  
    			return 1;  
    		}  
      
    		// Make the socket non-blocking  
    		SetBlockingMode(ClientSocket, FALSE);  
      
    		// Receive the data  
    		ReceivedData.clear();  
    		do {  
    			memset(szRecvBuffer, 0x00, sizeof(szRecvBuffer));  
    			iResult = recv(ClientSocket, szRecvBuffer, sizeof(szRecvBuffer) - 1, 0);  
    			if (iResult > 0) {  
    				ReceivedData.append(szRecvBuffer);  
    			}  
    		} while (iResult != SOCKET_ERROR);  
      
    		// determine the current time  
    		GetLocalTime(&lt);  
      
    		// format the time into a localized string  
    		strcpy_s(szMsgText, sizeof(szMsgText), "Current Timestamp: ");  
    		GetDateFormatA(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &lt, NULL, szTemp, sizeof(szTemp));  
    		strcat_s(szMsgText, sizeof(szMsgText), szTemp);  
    		strcat_s(szMsgText, sizeof(szMsgText), " ");  
    		GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_NOTIMEMARKER + TIME_FORCE24HOURFORMAT, &lt, NULL, szTemp, sizeof(szTemp));  
    		strcat_s(szMsgText, sizeof(szMsgText), szTemp);  
    		sprintf_s(szTemp, sizeof(szTemp), ".%03d", lt.wMilliseconds);  
    		strcat_s(szMsgText, sizeof(szMsgText), szTemp);  
    		strcat_s(szMsgText, sizeof(szMsgText), "\r\n");  
      
    		// Send reply  
    		SendResponse(ClientSocket, "HTTP/1.1 200 OK\r\n");  
    		SendResponse(ClientSocket, "Content-Type: text/plain; charset=UTF-8\r\n");  
    		SendResponse(ClientSocket, "\r\n");  
    		SendResponse(ClientSocket, szMsgText);  
    		if (ReceivedData.length() > 0) {  
    			SendResponse(ClientSocket, "\r\n");  
    			SendResponse(ClientSocket, "ReceivedData:\r\n");  
    			SendResponse(ClientSocket, "\r\n");  
    			SendResponse(ClientSocket, ReceivedData.c_str());  
    		}  
      
    		// Shutdown client socket  
    		iResult = shutdown(ClientSocket, SD_SEND);  
      
    		// Close client socket  
    		closesocket(ClientSocket);  
    	}  
      
    	// Close listen socket  
    	closesocket(ListenSocket);  
      
    	// Cleanly shut down Winsock  
    	WSACleanup();  
      
    	return 0;  
    }  
      
    
    0 comments No comments

  4. Roland Smith 1 Reputation point
    2022-06-17T19:23:31.257+00:00

    My server app currently is FastCGI written in C++. It supports GET from a web browser, returning either XML, JSON, or a PDF report. It also supports a desktop app written in C++ that uses WinHTTP to send parameters to code in the server app which uses the parameters to retrieve data from a database and return it to the client.

    I had posted about a fairly minor issue on StackOverflow and one of their experts suggested re-writing it using httpPlatformHandler. Conceptually it seems easy and has some advantages specific to my app but it doesn't seem to be working the way I want. The recv function sometimes returns nothing, sometimes the caller gets a Bad Gateway error, and I can't figure out how to access posted data. There are zero examples on the web that I can find.

    Does HTTP Server API handle automatically running multiple copies of my app depending on load? That is something FastCGI and httpPlatformHandler does for me.


  5. Bruce (SqlWork.com) 57,241 Reputation points
    2022-06-17T19:44:13.893+00:00

    couple issues with this code:

        ReceivedData.clear();  
             do {  
                 memset(szRecvBuffer, 0x00, sizeof(szRecvBuffer));  
                 iResult = recv(ClientSocket, szRecvBuffer, sizeof(szRecvBuffer) - 1, 0);  
                 if (iResult > 0) {  
                     ReceivedData.append(szRecvBuffer);  
                 }  
             } while (iResult != SOCKET_ERROR);  
    

    not sure it handles a partial buffer. but the big issue is it not getting the content size to know when to stop reading. your logic should read and parse the headers first. this will tell you lots of information about what is next in the stream (maybe another request)

    1) the content type and size
    2) whether this is a range request
    3) whether this is chunked request
    4) the encoding of the request data if any.