PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/HttpRequest.pm

https://github.com/yizumi/zbserver
Perl | 212 lines | 184 code | 25 blank | 3 comment | 21 complexity | 058d6904ab367f122039373c1b1fe1c0 MD5 | raw file
  1. {
  2. package HttpRequest;
  3. use JSON;
  4. use Digest::MD5 qw( md5 );
  5. my $init = 0;
  6. my $cache_subscription_header = "Content-Type: application/json\nCache-Control: non-cache\nProgma: no-cache\n\n";
  7. my $path = "./htdocs";
  8. my $MIME = {
  9. "ico" => "image/x-icon",
  10. "html" => "text/html",
  11. "txt" => "text/plain",
  12. "png" => "image/png",
  13. "jpeg" => "image/jpeg",
  14. "jpg" => "image/jpg",
  15. "gif" => "image/gif",
  16. "css" => "text/css",
  17. "js" => "application/x-javascript",
  18. "manifest" => "text/cache-manifest"
  19. };
  20. sub init
  21. {
  22. open FH, "< $path/SubscriptionHeader.html";
  23. while( <FH> ) {
  24. $cache_subscription_header .= $_;
  25. }
  26. close FH;
  27. }
  28. sub new
  29. {
  30. my( $klass, $client_id ) = @_;
  31. if( !$init ) {
  32. init();
  33. $init = 1;
  34. }
  35. my %headers = ();
  36. return bless {
  37. client_id => $client_id,
  38. mode => undef, # listener | publisher
  39. state => "EMPTY", # EMPTY | HEADER | READY | DISCONNECT
  40. command => undef, # e.g. "GET / HTTP/1.1"
  41. method => undef, # GET | POST
  42. uri => undef, # e.g. "/", "/submit", "/subscribe"
  43. http_version => undef, # e.g. "1.1"
  44. headers => \%headers,
  45. lastMessageIndex => -1 # no request for all messages
  46. }, $klass;
  47. }
  48. sub appendHeader
  49. {
  50. my( $self, $input ) = @_;
  51. return 1 if( $self->{state} eq "READY" );
  52. if( $self->{state} eq "EMPTY" )
  53. {
  54. if( $input =~ m/(GET|POST) ([^\s]+) HTTP\/([0-9.]+)/i )
  55. {
  56. $self->{mode} = "subscriber";
  57. $self->{command} = trim($0);
  58. $self->{method} = uc(trim($1));
  59. $self->{uri} = trim($2);
  60. $self->{http_version} = trim($3);
  61. if( $self->{uri} =~ /(.*)\?(.*)/) {
  62. $self->{uri} = $1;
  63. $self->{queryString} = $2;
  64. if( $self->{uri} eq "/subscribe" && $self->{queryString} =~/lastMessageIndex=([0-9]+)/ ) {
  65. $self->{lastMessageIndex} = $1 * 1;
  66. }
  67. }
  68. $self->{state} = "HEADER";
  69. return 1;
  70. }
  71. elsif( trim($input) eq "PUBLISHER" )
  72. {
  73. $self->{mode} = "publisher";
  74. $self->{state} = "READY";
  75. }
  76. }
  77. elsif( $self->{state} eq "HEADER" ) {
  78. if( $input =~ m/^([^:]+):(.*)\r\n / ) {
  79. my $key = trim($1);
  80. my $value = $2; # leave this untouched
  81. $self->{headers}->{lc($key)} = $value;
  82. return 1;
  83. }
  84. elsif( trim($input) eq "" ) {
  85. if( $self->{method} eq "GET" ) {
  86. # print("**** GET ... turning into READY\n" );
  87. $self->{state} = "READY";
  88. return 1;
  89. }
  90. elsif( $self->{method} eq "POST" ) {
  91. # print("**** POST ... turning into BODY\n" );
  92. $self->{state} = "READY";
  93. $self->{body} = "";
  94. return 1;
  95. }
  96. }
  97. }
  98. elsif( $self->{state} eq "BODY" ) {
  99. $self->{body} .= $input;
  100. print( "*** K... seeing $input\n" );
  101. if( length($self->{body}) >= $self->{headers}->{"content-length"} || index($input,"\x00") > -1 ) {
  102. print( "*** K... Done!\n" );
  103. $self->{state} = "READY";
  104. }
  105. else {
  106. print( "*** Still reading..." . length($self->{body}) . " out of " . $self->{headers}->{"content-length"} . " bytes\n" );
  107. }
  108. return 1;
  109. }
  110. else {
  111. return 0;
  112. }
  113. }
  114. sub handShakeKey
  115. {
  116. my( $self ) = @_;
  117. my $strkey1 = $self->{header}->{"sec-websocket-key1"};
  118. my $strkey2 = $self->{header}->{"sec-websocket-key2"};
  119. my $numkey1 = $strkey1; $numkey1 =~ s/[^\d]//g; $numkey1 *= 1;
  120. my $numkey2 = $strkey2; $numkey2 =~ s/[^\d]//g; $numkey2 *= 1;
  121. my $spaces1 = $strkey1; $spaces1 =~ s/[^\ ]//g; $spaces1 = length( $spaces1 );
  122. my $spaces2 = $strkey2; $spaces2 =~ s/[^\ ]//g; $spaces2 = length( $spaces2 );
  123. print( "strkey1: $strkey1\n" );
  124. print( "strkey2: $strkey2\n" );
  125. print( "numkey1: $numkey1\n" );
  126. print( "numkey2: $numkey2\n" );
  127. print( "spaces1: $spaces1\n" );
  128. print( "spaces1: $spaces2\n" );
  129. if( $spaces1 == 0 ||
  130. $spaces2 == 0 ||
  131. $numkey1 % $spaces1 != 0 ||
  132. $numkey2 % $spaces2 != 0 ) {
  133. print( "WebSocket contained an invalid key -- closing the connection.\n" );
  134. return 0;
  135. }
  136. # my $hash = md5( $
  137. }
  138. sub isHeader
  139. {
  140. my( $self, $input ) = @_;
  141. return $input =~ m/(GET|POST) ([^\s]+) HTTP\/([0-9.]+)/i;
  142. }
  143. sub isReady
  144. {
  145. my( $self ) = @_;
  146. return $self->{state} eq "READY";
  147. }
  148. sub getStaticContent
  149. {
  150. my( $self, $file ) = @_;
  151. my( $ext ) = $file =~ /\.([A-Z0-9]+)$/i;
  152. my $size = -s "$path/$file";
  153. my $data = "HTTP/".$self->{http_version}." 200 OK\nContent-Type: ".
  154. $MIME->{$ext}."\nContent-Length: $size\nCache-Control: non-cache\nProgma: no-chace\nExpires: Thu, 01 Dec 1994 16:00:00 GMT\n\n";
  155. open FH, "< $path/$file";
  156. while( <FH> ) {
  157. $data .= $_;
  158. }
  159. close FH;
  160. return $data;
  161. }
  162. sub getSubsriptionHeader
  163. {
  164. my( $self ) = @_;
  165. return "HTTP/".$self->{http_version} . " 200 OK\n" . $cache_subscription_header;
  166. }
  167. sub toString
  168. {
  169. my( $self ) = @_;
  170. my $obj = {
  171. command => $self->{command},
  172. method => $self->{method},
  173. uri => $self->{uri},
  174. http_version => $self->{http_version},
  175. headers => $self->{headers}
  176. };
  177. return to_json( $obj );
  178. }
  179. sub trim
  180. {
  181. my $string = shift;
  182. $string =~ s/^\s+//;
  183. $string =~ s/\s+$//;
  184. return $string;
  185. }
  186. }
  187. 1;