/* ** JNetLib ** Copyright (C) 2001 Nullsoft, Inc. ** Author: Justin Frankel ** File: wac_network_http_server.cpp - JNL HTTP GET/POST serving implementation ** License: see jnetlib.h ** ** This class just manages the http reply/sending, not where the data ** comes from, etc. */ #include "netinc.h" #include "util.h" #include "wac_network_http_server.h" /* States for m_state: -1 error (connection closed, etc) 0 not read request yet. 1 reading headers 2 headers read, have not sent reply 3 sent reply 4 closed */ wa::Components::WAC_Network_HTTP_Server::WAC_Network_HTTP_Server( WAC_Network_Connection *con ) { m_con = con; m_state = 0; m_reply_headers = 0; m_reply_string = 0; m_recv_request = 0; m_errstr = 0; m_reply_ready = 0; m_method = 0; http_ver = 0; keep_alive = 0; } wa::Components::WAC_Network_HTTP_Server::~WAC_Network_HTTP_Server() { free( m_recv_request ); free( m_reply_string ); free( m_reply_headers ); free( m_errstr ); free( m_method ); m_con->Release(); } static size_t strlen_whitespace( const char *str ) { size_t size = 0; while ( str && *str && *str != ' ' && *str != '\r' && *str != '\n' ) { str++; size++; } return size; } int wa::Components::WAC_Network_HTTP_Server::run() { // returns: < 0 on error, 0 on connection close, 1 if reading request, 2 if reply not sent, 3 if reply sent, sending data. int cnt = 0; run_again: m_con->run(); if ( m_con->get_state() == WAC_Network_Connection::STATE_ERROR ) { seterrstr( m_con->get_errstr() ); return -1; } if ( m_con->get_state() == WAC_Network_Connection::STATE_CLOSED ) return 4; if ( m_state == 0 ) { if ( m_con->recv_lines_available() > 0 ) { char *buf = (char *)malloc( m_con->recv_bytes_available() - 1 ); m_con->recv_line( buf, m_con->recv_bytes_available() - 1 ); free( m_recv_request ); m_recv_request = (char *)malloc( strlen( buf ) + 2 ); strcpy( m_recv_request, buf ); m_recv_request[ strlen( m_recv_request ) + 1 ] = 0; free( buf ); buf = m_recv_request; while ( buf && *buf ) buf++; while ( buf >= m_recv_request && *buf != ' ' ) buf--; if ( strncmp( buf + 1, "HTTP", 4 ) )// || strncmp(m_recv_request,"GET ",3)) { seterrstr( "malformed HTTP request" ); m_state = -1; } else { http_ver = atoi( buf + 8 ); size_t method_len = strlen_whitespace( m_recv_request ); m_method = (char *)malloc( method_len + 1 ); memcpy( m_method, m_recv_request, method_len ); m_method[ method_len ] = 0; m_state = 1; cnt = 0; if ( buf >= m_recv_request ) buf[ 0 ] = buf[ 1 ] = 0; buf = strstr( m_recv_request, "?" ); if ( buf ) { *buf++ = 0; // change &'s into 0s now. char *t = buf; int stat = 1; while ( t && *t ) { if ( *t == '&' && !stat ) { stat = 1; *t = 0; } else stat = 0; t++; } } } } else if ( !cnt++ ) goto run_again; } if ( m_state == 1 ) { if ( !cnt++ && m_con->recv_lines_available() < 1 ) goto run_again; while ( m_con->recv_lines_available() > 0 ) { char buf[ 4096 ] = { 0 }; m_con->recv_line( buf, 4096 ); if ( !buf[ 0 ] ) { m_state = 2; break; } recvheaders.Add( buf ); } } if ( m_state == 2 ) { if ( m_reply_ready ) { // send reply m_con->send_string( (char *)( m_reply_string ? m_reply_string : "HTTP/1.1 200 OK" ) ); m_con->send_string( "\r\n" ); if ( m_reply_headers ) m_con->send_string( m_reply_headers ); m_con->send_string( "\r\n" ); m_state = 3; } } if ( m_state == 3 ) { // nothing. } return m_state; } const char *wa::Components::WAC_Network_HTTP_Server::get_request_file() { // file portion of http request if ( !m_recv_request ) return NULL; char *t = m_recv_request; while ( t && *t && *t != ' ' ) t++; if ( !t || !*t ) return NULL; while ( t && *t && *t == ' ' ) t++; return t; } const char *wa::Components::WAC_Network_HTTP_Server::get_request_parm( const char *parmname ) // parameter portion (after ?) { const char *t = m_recv_request; while ( t && *t ) t++; if ( t ) t++; while ( t && *t ) { while ( t && *t && *t == '&' ) t++; if ( !_strnicmp( t, parmname, strlen( parmname ) ) && t[ strlen( parmname ) ] == '=' ) { return t + strlen( parmname ) + 1; } t += strlen( t ) + 1; } return NULL; } const char *wa::Components::WAC_Network_HTTP_Server::getheader( const char *headername ) { return recvheaders.GetHeader( headername ); } void wa::Components::WAC_Network_HTTP_Server::set_reply_string( const char *reply_string ) // should be HTTP/1.1 OK or the like { free( m_reply_string ); m_reply_string = (char *)malloc( strlen( reply_string ) + 1 ); strcpy( m_reply_string, reply_string ); } void wa::Components::WAC_Network_HTTP_Server::add_reply_header( const char *header ) // "Connection: close" for example { // if they've specified a content-length, then we can keep alive an HTTP/1.1 connection if ( !keep_alive && http_ver == 1 && !_strnicmp( header, "Content-Length", 14 ) ) keep_alive = 1; if ( m_reply_headers ) { char *tmp = (char *)malloc( strlen( m_reply_headers ) + strlen( header ) + 3 ); strcpy( tmp, m_reply_headers ); strcat( tmp, header ); strcat( tmp, "\r\n" ); free( m_reply_headers ); m_reply_headers = tmp; } else { m_reply_headers = (char *)malloc( strlen( header ) + 3 ); strcpy( m_reply_headers, header ); strcat( m_reply_headers, "\r\n" ); } } void wa::Components::WAC_Network_HTTP_Server::reset() { free( m_recv_request ); m_recv_request = 0; free( m_reply_string ); m_reply_string = 0; free( m_reply_headers ); m_reply_headers = 0; free( m_errstr ); m_errstr = 0; free( m_method ); m_method = 0; m_reply_ready = 0; m_state = 0; keep_alive = 0; } #ifdef CBCLASS #undef CBCLASS #endif #define CBCLASS wa::Components::WAC_Network_HTTP_Server START_DISPATCH; CB( API_HTTPSERV_RUN, run ); CB( API_HTTPSERV_GETERRSTR, geterrorstr ); CB( API_HTTPSERV_GETREQUESTFILE, get_request_file ); CB( API_HTTPSERV_GETREQUESTPARM, get_request_parm ); CB( API_HTTPSERV_GETALLHEADERS, getallheaders ); CB( API_HTTPSERV_GETHEADER, getheader ); VCB( API_HTTPSERV_SETREPLYSTR, set_reply_string ); VCB( API_HTTPSERV_SETREPLYHEADER, set_reply_header ); VCB( API_HTTPSERV_SENDREPLY, send_reply ); CB( API_HTTPSERV_BYTESINQUEUE, bytes_inqueue ); CB( API_HTTPSERV_BYTESCANSEND, bytes_cansend ); VCB( API_HTTPSERV_WRITEBYTES, write_bytes ); VCB( API_HTTPSERV_CLOSE, close ); CB( API_HTTPSERV_GETCON, get_con ); CB( API_HTTPSERV_GETMETHOD, get_method ); END_DISPATCH;