/[cwmp]/google/trunk/lib/CWMP/Session.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /google/trunk/lib/CWMP/Session.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 48 - (show annotations)
Tue Jun 19 20:02:36 2007 UTC (16 years, 11 months ago) by dpavlin
Original Path: google/lib/CWMP/Server.pm
File size: 4941 byte(s)
document every method and add tests for pod
1 # Dobrica Pavlinusic, <dpavlin@rot13.org> 06/18/07 10:19:50 CEST
2 package CWMP::Server;
3
4 use strict;
5 use warnings;
6
7 use base qw/Class::Accessor/;
8 __PACKAGE__->mk_accessors( qw/
9 debug
10 port
11 sock
12 / );
13
14 use IO::Socket::INET;
15 use Data::Dump qw/dump/;
16 use CWMP::Request;
17 use CWMP::Response;
18 use Carp qw/confess cluck/;
19
20 =head1 NAME
21
22 CWMP::Server - implement logic of CWMP protocol
23
24 =head1 METHODS
25
26 =head2 new
27
28 my $server = CWMP::Server->new({ port => 3333 });
29
30 =head2 run
31
32 $server->run();
33
34 =cut
35
36 sub run {
37 my $self = shift;
38
39 my $listen = IO::Socket::INET->new(
40 Listen => 5,
41 # LocalAddr => 'localhost',
42 LocalPort => $self->port,
43 Proto => 'tcp',
44 Blocking => 1,
45 ReuseAddr => 1,
46 );
47
48 warn "waiting for request on port ", $self->port, $/;
49
50 while ( my $sock = $listen->accept ) {
51 $sock->autoflush(1);
52
53 warn "connection from ", $sock->peerhost, "\n";
54
55 $self->sock( $sock ); # FIXME this will not work for multiple clients
56 while ( $self->process_request ) {
57 warn "...another one bites a dust...\n";
58 }
59
60 warn "...returning to accepting new connections\n";
61 }
62 }
63
64 =head2 process_request
65
66 One request from client/response from server cycle. Call multiple times to
67 facilitate brain-dead concept of adding state to stateless protocol like
68 HTTP.
69
70 =cut
71
72 sub process_request {
73 my $self = shift;
74
75 my $sock = $self->sock || die "no sock?";
76
77 die "not IO::Socket::INET but ", ref( $sock ) unless ( ref($sock) eq 'IO::Socket::INET' );
78
79 if ( ! $sock->connected ) {
80 warn "SOCKET NOT CONNECTED";
81 return 0;
82 }
83
84 $sock->autoflush( 1 );
85 $sock->blocking( 1 );
86
87 ### read the first line of response
88 my $line = $sock->getline || return $self->error(400, "No Data");
89
90 $line =~ s/[\r\n]+$//;
91 if ($line !~ /^ (\w+) \ + (\S+) \ + (HTTP\/1.\d) $ /x) {
92 return $self->error(400, "Bad request");
93 }
94 my ($method, $req, $protocol) = ($1, $2, $3);
95 warn "<<<< ",join(" ", time, $method, $req)."\n";
96
97 ### read in other headers
98 $self->read_headers || return $self->error(400, "Strange headers");
99
100 ### do we support the type
101 # if ($method !~ /GET|POST|HEAD/) {
102 if ($method !~ /POST/) {
103 return $self->error(400, "Unsupported Method");
104 }
105
106 my $chunk;
107 my $transfer_encoding = $self->header('Transfer-Encoding');
108
109 if ( $transfer_encoding && $transfer_encoding =~ qr/^chunked/i ) {
110
111 my $len = 0;
112
113 do {
114
115 warn "get chunk len\n" if $self->debug;
116
117 my $hex;
118 do {
119 $hex = $sock->getline;
120 $hex =~ s/[\n\r]+$//;
121 } until ( $hex ne '' );
122
123 die "chunk size not valid hex: $hex" unless ( $hex =~ m/^[0-9a-f]+$/i);
124 $len = hex( $hex );
125
126 warn "getting chunk of $len bytes\n" if $self->debug;
127
128 $sock->read( my $buff, $len );
129 $chunk .= $buff;
130
131 warn "--- $len bytes: --=>||$buff||<=--\n";
132
133 } while ( $len > 0 );
134
135 } else {
136 die "right now, we support only Transfer-Encoding: chunked";
137 }
138
139 warn "handler got ", length($chunk), " bytes\n" if $self->debug;
140
141 warn "<<< " . localtime() . " " . $sock->peerhost . "\n";
142
143 die "not SOAP request" unless defined ( $self->header('SOAPAction') );
144
145 my $state;
146
147 if ( $chunk ) {
148 warn "## request chunk: ",length($chunk)," bytes\n$chunk\n" if $self->debug;
149
150 $state = CWMP::Request->parse( $chunk );
151
152 warn "acquired state = ", dump( $state ), "\n";
153
154 } else {
155 warn "empty request\n";
156 }
157
158
159 my $response = CWMP::Response->new({ debug => $self->debug });
160
161 $sock->send(join("\r\n",
162 'HTTP/1.1 200 OK',
163 'Content-Type: text/xml; charset="utf-8"',
164 'Server: AcmeCWMP/42',
165 'SOAPServer: AcmeCWMP/42',
166 ));
167
168 $sock->send( "Set-Cookie: ID=" . $state->{ID} . "; path=/\r\n" ) if ( $state->{ID} );
169
170 my $xml = '';
171
172 if ( my $dispatch = $state->{_dispatch} ) {
173 if ( $response->can( $dispatch ) ) {
174 warn ">>> dispatching to $dispatch\n";
175 $xml = $response->$dispatch( $state ) . "\r\n";
176 warn "## response payload: ",length($xml)," bytes\n$xml\n";
177 } else {
178 confess "can't dispatch to $dispatch";
179 }
180 } else {
181 warn ">>> empty response\n";
182 }
183
184 $sock->send( "Content-Length: " . length( $xml ) . "\r\n\r\n" );
185 $sock->send( $xml ) or die "can't send response";
186
187 warn "### request over";
188
189 };
190
191 =head2 read_headers
192
193 parse headers from request
194
195 =cut
196
197 sub read_headers {
198 my $self = shift;
199
200 $self->{headers} = {};
201
202 while (defined($_ = $self->sock->getline)) {
203 s/[\r\n]+$//;
204 last unless length $_;
205 warn "-- $_\n";
206 return 0 if ! /^ ([\w\-]+) :[\ \t]* (.*) $/x;
207 $self->{headers}->{$1} = $2;
208 }
209
210 return 1;
211 }
212
213 =head2 header
214
215 Getter for specific header
216
217 $self->header('Cookies');
218
219 =cut
220
221 sub header {
222 my $self = shift;
223 my $header = shift || die "no header?";
224 if ( defined( $self->{headers}->{$header} )) {
225 return $self->{headers}->{$header};
226 } else {
227 return;
228 }
229 }
230
231 =head2 error
232
233 return $self->error( 501, 'System error' );
234
235 =cut
236
237 sub error {
238 my ($self, $number, $msg) = @_;
239 $msg ||= 'ERROR';
240 $self->sock->send( "HTTP/1.1 $number $msg\r\n" );
241 warn "Error - $number - $msg\n";
242 return 0; # close connection
243 }
244
245 1;

  ViewVC Help
Powered by ViewVC 1.1.26