Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

443 lines
12 KiB

  1. # $Id: Common.pm,v 1.19 2001/01/05 18:53:11 gisle Exp $
  2. #
  3. package HTTP::Request::Common;
  4. use strict;
  5. use vars qw(@EXPORT @EXPORT_OK $VERSION $DYNAMIC_FILE_UPLOAD);
  6. $DYNAMIC_FILE_UPLOAD ||= 0; # make it defined (don't know why)
  7. require Exporter;
  8. *import = \&Exporter::import;
  9. @EXPORT =qw(GET HEAD PUT POST);
  10. @EXPORT_OK = qw($DYNAMIC_FILE_UPLOAD);
  11. require HTTP::Request;
  12. use Carp();
  13. $VERSION = sprintf("%d.%02d", q$Revision: 1.19 $ =~ /(\d+)\.(\d+)/);
  14. my $CRLF = "\015\012"; # "\r\n" is not portable
  15. sub GET { _simple_req('GET', @_); }
  16. sub HEAD { _simple_req('HEAD', @_); }
  17. sub PUT { _simple_req('PUT' , @_); }
  18. sub POST
  19. {
  20. my $url = shift;
  21. my $req = HTTP::Request->new(POST => $url);
  22. my $content;
  23. $content = shift if @_ and ref $_[0];
  24. my($k, $v);
  25. while (($k,$v) = splice(@_, 0, 2)) {
  26. if (lc($k) eq 'content') {
  27. $content = $v;
  28. } else {
  29. $req->push_header($k, $v);
  30. }
  31. }
  32. my $ct = $req->header('Content-Type');
  33. unless ($ct) {
  34. $ct = 'application/x-www-form-urlencoded';
  35. } elsif ($ct eq 'form-data') {
  36. $ct = 'multipart/form-data';
  37. }
  38. if (ref $content) {
  39. if ($ct =~ m,^multipart/form-data\s*(;|$),i) {
  40. require HTTP::Headers::Util;
  41. my @v = HTTP::Headers::Util::split_header_words($ct);
  42. Carp::carp("Multiple Content-Type headers") if @v > 1;
  43. @v = @{$v[0]};
  44. my $boundary;
  45. my $boundary_index;
  46. for (my @tmp = @v; @tmp;) {
  47. my($k, $v) = splice(@tmp, 0, 2);
  48. if (lc($k) eq "boundary") {
  49. $boundary = $v;
  50. $boundary_index = @v - @tmp - 1;
  51. last;
  52. }
  53. }
  54. ($content, $boundary) = form_data($content, $boundary, $req);
  55. if ($boundary_index) {
  56. $v[$boundary_index] = $boundary;
  57. } else {
  58. push(@v, boundary => $boundary);
  59. }
  60. $ct = HTTP::Headers::Util::join_header_words(@v);
  61. } else {
  62. # We use a temporary URI object to format
  63. # the application/x-www-form-urlencoded content.
  64. require URI;
  65. my $url = URI->new('http:');
  66. $url->query_form(ref($content) eq "HASH" ? %$content : @$content);
  67. $content = $url->query;
  68. }
  69. }
  70. $req->header('Content-Type' => $ct); # might be redundant
  71. if (defined($content)) {
  72. $req->header('Content-Length' =>
  73. length($content)) unless ref($content);
  74. $req->content($content);
  75. }
  76. $req;
  77. }
  78. sub _simple_req
  79. {
  80. my($method, $url) = splice(@_, 0, 2);
  81. my $req = HTTP::Request->new($method => $url);
  82. my($k, $v);
  83. while (($k,$v) = splice(@_, 0, 2)) {
  84. if (lc($k) eq 'content') {
  85. $req->add_content($v);
  86. } else {
  87. $req->push_header($k, $v);
  88. }
  89. }
  90. $req;
  91. }
  92. sub form_data # RFC1867
  93. {
  94. my($data, $boundary, $req) = @_;
  95. my @data = ref($data) eq "HASH" ? %$data : @$data; # copy
  96. my $fhparts;
  97. my @parts;
  98. my($k,$v);
  99. while (($k,$v) = splice(@data, 0, 2)) {
  100. if (!ref($v)) {
  101. $k =~ s/([\\\"])/\\$1/g; # escape quotes and backslashes
  102. push(@parts,
  103. qq(Content-Disposition: form-data; name="$k"$CRLF$CRLF$v));
  104. } else {
  105. my($file, $usename, @headers) = @$v;
  106. unless (defined $usename) {
  107. $usename = $file;
  108. $usename =~ s,.*/,, if defined($usename);
  109. }
  110. my $disp = qq(form-data; name="$k");
  111. $disp .= qq(; filename="$usename") if $usename;
  112. my $content = "";
  113. my $h = HTTP::Headers->new(@headers);
  114. my $ct = $h->header("Content-Type");
  115. if ($file) {
  116. require Symbol;
  117. my $fh = Symbol::gensym();
  118. open($fh, $file) or Carp::croak("Can't open file $file: $!");
  119. binmode($fh);
  120. if ($DYNAMIC_FILE_UPLOAD) {
  121. # will read file later
  122. $content = $fh;
  123. } else {
  124. local($/) = undef; # slurp files
  125. $content = <$fh>;
  126. close($fh);
  127. $h->header("Content-Length" => length($content));
  128. }
  129. unless ($ct) {
  130. require LWP::MediaTypes;
  131. $ct = LWP::MediaTypes::guess_media_type($file, $h);
  132. }
  133. }
  134. if ($h->header("Content-Disposition")) {
  135. # just to get it sorted first
  136. $disp = $h->header("Content-Disposition");
  137. $h->remove_header("Content-Disposition");
  138. }
  139. if ($h->header("Content")) {
  140. $content = $h->header("Content");
  141. $h->remove_header("Content");
  142. }
  143. my $head = join($CRLF, "Content-Disposition: $disp",
  144. $h->as_string($CRLF),
  145. "");
  146. if (ref $content) {
  147. push(@parts, [$head, $content]);
  148. $fhparts++;
  149. } else {
  150. push(@parts, $head . $content);
  151. }
  152. }
  153. }
  154. return "" unless @parts;
  155. my $content;
  156. if ($fhparts) {
  157. $boundary = boundary(10) # hopefully enough randomness
  158. unless $boundary;
  159. # add the boundaries to the @parts array
  160. for (1..@parts-1) {
  161. splice(@parts, $_*2-1, 0, "$CRLF--$boundary$CRLF");
  162. }
  163. unshift(@parts, "--$boundary$CRLF");
  164. push(@parts, "$CRLF--$boundary--$CRLF");
  165. # See if we can generate Content-Length header
  166. my $length = 0;
  167. for (@parts) {
  168. if (ref $_) {
  169. my ($head, $f) = @$_;
  170. my $file_size;
  171. unless ( -f $f && ($file_size = -s _) ) {
  172. # The file is either a dynamic file like /dev/audio
  173. # or perhaps a file in the /proc file system where
  174. # stat may return a 0 size even though reading it
  175. # will produce data. So we cannot make
  176. # a Content-Length header.
  177. undef $length;
  178. last;
  179. }
  180. $length += $file_size + length $head;
  181. } else {
  182. $length += length;
  183. }
  184. }
  185. $length && $req->header('Content-Length' => $length);
  186. # set up a closure that will return content piecemeal
  187. $content = sub {
  188. for (;;) {
  189. unless (@parts) {
  190. defined $length && $length != 0 &&
  191. Carp::croak "length of data sent did not match calculated Content-Length header. Probably because uploaded file changed in size during transfer.";
  192. return;
  193. }
  194. my $p = shift @parts;
  195. unless (ref $p) {
  196. $p .= shift @parts while @parts && !ref($parts[0]);
  197. defined $length && ($length -= length $p);
  198. return $p;
  199. }
  200. my($buf, $fh) = @$p;
  201. my $buflength = length $buf;
  202. my $n = read($fh, $buf, 2048, $buflength);
  203. if ($n) {
  204. $buflength += $n;
  205. unshift(@parts, ["", $fh]);
  206. } else {
  207. close($fh);
  208. }
  209. if ($buflength) {
  210. defined $length && ($length -= $buflength);
  211. return $buf
  212. }
  213. }
  214. };
  215. } else {
  216. $boundary = boundary() unless $boundary;
  217. my $bno = 0;
  218. CHECK_BOUNDARY:
  219. {
  220. for (@parts) {
  221. if (index($_, $boundary) >= 0) {
  222. # must have a better boundary
  223. $boundary = boundary(++$bno);
  224. redo CHECK_BOUNDARY;
  225. }
  226. }
  227. last;
  228. }
  229. $content = "--$boundary$CRLF" .
  230. join("$CRLF--$boundary$CRLF", @parts) .
  231. "$CRLF--$boundary--$CRLF";
  232. }
  233. wantarray ? ($content, $boundary) : $content;
  234. }
  235. sub boundary
  236. {
  237. my $size = shift || return "xYzZY";
  238. require MIME::Base64;
  239. my $b = MIME::Base64::encode(join("", map chr(rand(256)), 1..$size*3), "");
  240. $b =~ s/[\W]/X/g; # ensure alnum only
  241. $b;
  242. }
  243. 1;
  244. __END__
  245. =head1 NAME
  246. HTTP::Request::Common - Construct common HTTP::Request objects
  247. =head1 SYNOPSIS
  248. use HTTP::Request::Common;
  249. $ua = LWP::UserAgent->new;
  250. $ua->request(GET 'http://www.sn.no/');
  251. $ua->request(POST 'http://somewhere/foo', [foo => bar, bar => foo]);
  252. =head1 DESCRIPTION
  253. This module provide functions that return newly created HTTP::Request
  254. objects. These functions are usually more convenient to use than the
  255. standard HTTP::Request constructor for these common requests. The
  256. following functions are provided.
  257. =over 4
  258. =item GET $url, Header => Value,...
  259. The GET() function returns a HTTP::Request object initialized with the
  260. GET method and the specified URL. Without additional arguments it
  261. is exactly equivalent to the following call
  262. HTTP::Request->new(GET => $url)
  263. but is less cluttered. It also reads better when used together with the
  264. LWP::UserAgent->request() method:
  265. my $ua = new LWP::UserAgent;
  266. my $res = $ua->request(GET 'http://www.sn.no')
  267. if ($res->is_success) { ...
  268. You can also initialize header values in the request by specifying
  269. some key/value pairs as optional arguments. For instance:
  270. $ua->request(GET 'http://www.sn.no',
  271. If_Match => 'foo',
  272. From => '[email protected]',
  273. );
  274. A header key called 'Content' is special and when seen the value will
  275. initialize the content part of the request instead of setting a header.
  276. =item HEAD $url, [Header => Value,...]
  277. Like GET() but the method in the request is HEAD.
  278. =item PUT $url, [Header => Value,...]
  279. Like GET() but the method in the request is PUT.
  280. =item POST $url, [$form_ref], [Header => Value,...]
  281. This works mostly like GET() with POST as the method, but this function
  282. also takes a second optional array or hash reference parameter
  283. ($form_ref). This argument can be used to pass key/value pairs for
  284. the form content. By default we will initialize a request using the
  285. C<application/x-www-form-urlencoded> content type. This means that
  286. you can emulate a HTML E<lt>form> POSTing like this:
  287. POST 'http://www.perl.org/survey.cgi',
  288. [ name => 'Gisle Aas',
  289. email => '[email protected]',
  290. gender => 'M',
  291. born => '1964',
  292. perc => '3%',
  293. ];
  294. This will create a HTTP::Request object that looks like this:
  295. POST http://www.perl.org/survey.cgi
  296. Content-Length: 66
  297. Content-Type: application/x-www-form-urlencoded
  298. name=Gisle%20Aas&email=gisle%40aas.no&gender=M&born=1964&perc=3%25
  299. The POST method also supports the C<multipart/form-data> content used
  300. for I<Form-based File Upload> as specified in RFC 1867. You trigger
  301. this content format by specifying a content type of C<'form-data'> as
  302. one of the request headers. If one of the values in the $form_ref is
  303. an array reference, then it is treated as a file part specification
  304. with the following interpretation:
  305. [ $file, $filename, Header => Value... ]
  306. The first value in the array ($file) is the name of a file to open.
  307. This file will be read and its content placed in the request. The
  308. routine will croak if the file can't be opened. Use an C<undef> as $file
  309. value if you want to specify the content directly. The $filename is
  310. the filename to report in the request. If this value is undefined,
  311. then the basename of the $file will be used. You can specify an empty
  312. string as $filename if you don't want any filename in the request.
  313. Sending my F<~/.profile> to the survey used as example above can be
  314. achieved by this:
  315. POST 'http://www.perl.org/survey.cgi',
  316. Content_Type => 'form-data',
  317. Content => [ name => 'Gisle Aas',
  318. email => '[email protected]',
  319. gender => 'M',
  320. born => '1964',
  321. init => ["$ENV{HOME}/.profile"],
  322. ]
  323. This will create a HTTP::Request object that almost looks this (the
  324. boundary and the content of your F<~/.profile> is likely to be
  325. different):
  326. POST http://www.perl.org/survey.cgi
  327. Content-Length: 388
  328. Content-Type: multipart/form-data; boundary="6G+f"
  329. --6G+f
  330. Content-Disposition: form-data; name="name"
  331. Gisle Aas
  332. --6G+f
  333. Content-Disposition: form-data; name="email"
  334. gisle@aas.no
  335. --6G+f
  336. Content-Disposition: form-data; name="gender"
  337. M
  338. --6G+f
  339. Content-Disposition: form-data; name="born"
  340. 1964
  341. --6G+f
  342. Content-Disposition: form-data; name="init"; filename=".profile"
  343. Content-Type: text/plain
  344. PATH=/local/perl/bin:$PATH
  345. export PATH
  346. --6G+f--
  347. If you set the $DYNAMIC_FILE_UPLOAD variable (exportable) to some TRUE
  348. value, then you get back a request object with a subroutine closure as
  349. the content attribute. This subroutine will read the content of any
  350. files on demand and return it in suitable chunks. This allow you to
  351. upload arbitrary big files without using lots of memory. You can even
  352. upload infinite files like F</dev/audio> if you wish; however, if
  353. the file is not a plain file, there will be no Content-Length header
  354. defined for the request. Not all servers (or server
  355. applications) like this. Also, if the file(s) change in size between
  356. the time the Content-Length is calculated and the time that the last
  357. chunk is delivered, the subroutine will C<Croak>.
  358. =back
  359. =head1 SEE ALSO
  360. L<HTTP::Request>, L<LWP::UserAgent>
  361. =head1 COPYRIGHT
  362. Copyright 1997-2000, Gisle Aas
  363. This library is free software; you can redistribute it and/or
  364. modify it under the same terms as Perl itself.
  365. =cut