📄 miniserver.cpp
字号:
// throws // OutOfMemoryException// MiniServerReadException// RCODE_NETWORK_READ_ERROR// RCODE_MALFORMED_LINE// RCODE_METHOD_NOT_IMPLEMENTED // RCODE_LENGTH_NOT_SPECIFIEDstatic void ReadRequest( int sockfd, xstring& document, HTTP_COMMAND_TYPE& command ){ NetReader1 reader( sockfd ); xstring reqLine; xstring line; bool newlineNotFound; int status; HTTP_COMMAND_TYPE cmd; int contentLength; const int BUFSIZE = 3; char buf[ BUFSIZE + 1 ]; MiniServerReadException excep; document = ""; // read request-line status = reader.getLine( reqLine, newlineNotFound ); if ( status < 0 ) { // read error excep.setErrorCode( RCODE_NETWORK_READ_ERROR ); throw excep; } if ( newlineNotFound ) { // format error excep.setErrorCode( RCODE_MALFORMED_LINE ); throw excep; } cmd = GetCommandType( reqLine ); if ( cmd == CMD_HTTP_UNKNOWN ) { // unknown or unsupported cmd excep.setErrorCode( RCODE_METHOD_NOT_IMPLEMENTED ); throw excep; } document += reqLine; contentLength = -1; // init // read headers while ( true ) { status = reader.getLine( line, newlineNotFound ); if ( status < 0 ) { // network error excep.setErrorCode( RCODE_NETWORK_READ_ERROR ); throw excep; } if ( newlineNotFound ) { // bad format excep.setErrorCode( RCODE_MALFORMED_LINE ); throw excep; } // get content-length if not obtained already if ( contentLength < 0 ) { bool malformed; contentLength = ParseContentLength( line, malformed ); if ( malformed ) { excep.setErrorCode( RCODE_MALFORMED_LINE ); throw excep; } } document += line; // done ? if ( line == "\n" || line == "\r\n" ) { break; } } // must have body for POST and M-POST msgs if ( contentLength < 0 && (cmd == CMD_SOAP_POST || cmd == CMD_SOAP_MPOST) ) { // HTTP: length reqd excep.setErrorCode( RCODE_LENGTH_NOT_SPECIFIED ); throw excep; } if ( contentLength > 0 ) { int totalBytesRead = 0; // read body while ( true ) { int bytesRead; bytesRead = reader.readData( buf, BUFSIZE ); if ( bytesRead > 0 ) { buf[ bytesRead ] = 0; // null terminate string document.appendLimited( buf, bytesRead ); totalBytesRead += bytesRead; if ( totalBytesRead >= contentLength ) { // done reading data break; } } else if ( bytesRead == 0 ) { // done break; } else { // error reading excep.setErrorCode( RCODE_NETWORK_READ_ERROR ); throw excep; } } } command = cmd;}// throws OutOfMemoryExceptionstatic void HandleError( int errCode, int sockfd ){ xstring errMsg; switch (errCode) { case RCODE_NETWORK_READ_ERROR: break; case RCODE_TIMEDOUT: break; case RCODE_MALFORMED_LINE: errMsg = "400 Bad Request"; break; case RCODE_LENGTH_NOT_SPECIFIED: errMsg = "411 Length Required"; break; case RCODE_METHOD_NOT_ALLOWED: errMsg = "405 Method Not Allowed"; break; case RCODE_INTERNAL_SERVER_ERROR: errMsg = "500 Internal Server Error"; break; case RCODE_METHOD_NOT_IMPLEMENTED: errMsg = "511 Not Implemented"; break; default: DBG( UpnpPrintf( UPNP_CRITICAL, MSERV, __FILE__, __LINE__, "HandleError: unknown code %d\n", errCode ); ) break; }; // no error msg to send; done if ( errMsg.length() == 0 ) return; xstring msg; msg = "HTTP/1.1 "; msg += errMsg; msg += "\r\n\r\n"; // send msg WriteNetData( msg.c_str(), sockfd ); // dbg // sleep so that client does get connection reset //sleep( 3 ); /////// // dbg DBG( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, "http error: %s\n", msg.c_str() ); ) ///////}// throws MiniServerReadException.RCODE_METHOD_NOT_IMPLEMENTEDstatic void MultiplexCommand( HTTP_COMMAND_TYPE cmd, const xstring& document, int sockfd ){ MiniServerCallback callback; switch ( cmd ) { case CMD_SOAP_POST: case CMD_SOAP_MPOST: callback = gSoapCallback; break; case CMD_GENA_NOTIFY: case CMD_GENA_SUBSCRIBE: case CMD_GENA_UNSUBSCRIBE: callback = gGenaCallback; break; case CMD_HTTP_GET: callback = gGetCallback; break; default: callback = NULL; } //DBG(printf("READ>>>>>>\n%s\n<<<<<<READ\n", document.c_str())); if ( callback == NULL ) { MiniServerReadException e( "callback not defined or unknown method" ); e.setErrorCode( RCODE_METHOD_NOT_IMPLEMENTED ); throw e; } callback( document.c_str(), sockfd );}static void HandleRequest( void *args ){ int sockfd; xstring document; HTTP_COMMAND_TYPE cmd; sockfd = (int) args; try { ReadRequest( sockfd, document, cmd ); // pass data to callback MultiplexCommand( cmd, document, sockfd ); //printf( "input document:\n%s\n", document.c_str() ); } catch ( MiniServerReadException& e ) { //DBG( e.print(); ) DBG( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, "error code = %d\n", e.getErrorCode()); ) HandleError( e.getErrorCode(), sockfd ); // destroy connection close( sockfd ); } catch ( ... ) { DBG( UpnpPrintf( UPNP_CRITICAL, MSERV, __FILE__, __LINE__, "HandleRequest(): unknown error\n"); ) close( sockfd ); }}static void RunMiniServer( void* args ){ struct sockaddr_in clientAddr; int listenfd; listenfd = (int)args; gMServThread = pthread_self(); gMServState = MSERV_RUNNING; try { while ( true ) { int connectfd; socklen_t clientLen; DBG( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, "Waiting...\n" ); ) // get a client request while ( true ) { // stop server if ( gMServState == MSERV_STOPPING ) { throw -9; } connectfd = accept( listenfd, (sockaddr*) &clientAddr, &clientLen ); if ( connectfd > 0 ) { // valid connection break; } if ( connectfd == -1 && errno == EINTR ) { // interrupted -- stop? if ( gMServState == MSERV_STOPPING ) { throw -9; } else { // ignore interruption continue; } } else { xstring errStr = "Error: RunMiniServer: accept(): "; errStr = strerror( errno ); throw BasicException( errStr.c_str() ); } } int sched_stat; sched_stat = tpool_Schedule( HandleRequest, (void*)connectfd ); if ( sched_stat < 0 ) { HandleError( RCODE_INTERNAL_SERVER_ERROR, connectfd ); } //HandleRequest( (void *)connectfd ); } } catch ( GenericException& e ) { //DBG( e.print(); ) } catch ( int code ) { if ( code == -9 ) { // stop miniserver assert( gMServState == MSERV_STOPPING ); DBG( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, "Miniserver: recvd STOP signal\n"); ) close( listenfd ); gMServState = MSERV_IDLE; gMServThread = 0; } }}// returns port to which socket, sockfd, is bound.// -1 on error; check errno// > 0 means port numberstatic int get_port( int sockfd ){ sockaddr_in sockinfo; socklen_t len; int code; int port; len = sizeof(sockinfo); code = getsockname( sockfd, (sockaddr*)&sockinfo, &len ); if ( code == -1 ) { return -1; } port = htons( sockinfo.sin_port ); DBG( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, "sockfd = %d, .... port = %d\n", sockfd, port ); ) return port;}// if listen port is 0, port is dynamically picked// returns:// on success: actual port socket is bound to// on error: a negative number UPNP_E_XXXint StartMiniServer( unsigned short listen_port ){ struct sockaddr_in serverAddr; int listenfd = 0; int success; int actual_port; int on =1; int retCode = 0; if ( gMServState != MSERV_IDLE ) { return UPNP_E_INTERNAL_ERROR; // miniserver running } try { //printf("listen port: %d\n",listen_port); listenfd = socket( AF_INET, SOCK_STREAM, 0 ); if ( listenfd <= 0 ) { throw UPNP_E_OUTOF_SOCKET; // error creating socket } bzero( &serverAddr, sizeof(serverAddr) ); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = htonl( INADDR_ANY ); serverAddr.sin_port = htons( listen_port ); //THIS IS ALLOWS US TO BIND AGAIN IMMEDIATELY //AFTER OUR SERVER HAS BEEN CLOSED //THIS MAY CAUSE TCP TO BECOME LESS RELIABLE //HOWEVER IT HAS BEEN SUGESTED FOR TCP SERVERS if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on, sizeof(int))==-1) { throw UPNP_E_SOCKET_BIND; } success = bind( listenfd, (sockaddr*)&serverAddr, sizeof(serverAddr) ); if ( success == -1 ) { throw UPNP_E_SOCKET_BIND; // bind failed } success = listen( listenfd, 10 ); if ( success == -1 ) { throw UPNP_E_LISTEN; // listen failed } actual_port = get_port( listenfd ); if ( actual_port <= 0 ) { throw UPNP_E_INTERNAL_ERROR; } success = tpool_Schedule( RunMiniServer, (void *)listenfd ); if ( success < 0 ) { throw UPNP_E_OUTOF_MEMORY; } // wait for miniserver to start while ( gMServState != MSERV_RUNNING ) { sleep(1); } retCode = actual_port; } catch ( int catchCode ) { retCode = catchCode; if ( listenfd != 0 ) { close( listenfd ); } } return retCode;}// returns 0: success; -2 if miniserver is idleint StopMiniServer( void ){ if ( gMServState == MSERV_IDLE ) return -2; gMServState = MSERV_STOPPING; // keep sending signals until server stops while ( true ) { if ( gMServState == MSERV_IDLE ) { break; } DBG( UpnpPrintf( UPNP_INFO, MSERV, __FILE__, __LINE__, "StopMiniServer(): sending interrupt\n"); ) int code = tintr_Interrupt( gMServThread ); if ( code < 0 ) { DBG( UpnpPrintf( UPNP_CRITICAL, MSERV, __FILE__, __LINE__, "%s: StopMiniServer(): interrupt failed", strerror(errno) ); ) //DBG( perror("StopMiniServer(): interrupt failed"); ) } if ( gMServState == MSERV_IDLE ) { break; } sleep( 1 ); // pause before signalling again } return 0;}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -