Skip to content

Commit b6121a1

Browse files
committed
add HTTP::Tiny mirror()
1 parent cd0c077 commit b6121a1

File tree

3 files changed

+66
-44
lines changed

3 files changed

+66
-44
lines changed

FEATURE_MATRIX.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@
306306
- ✔️ **Time::Local** module.
307307
- ✔️ **HTTP::Date** module.
308308
- ✔️ **HTTP::CookieJar** module.
309-
- 🚧 **HTTP::Tiny** `mirror` not implemented.
309+
- 🚧 **HTTP::Tiny** some features untested: proxy settings.
310310

311311

312312
## Non-strict and Obsolete Features

src/main/java/org/perlonjava/perlmodule/HttpTiny.java

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import javax.net.ssl.SSLContext;
99
import javax.net.ssl.TrustManager;
1010
import javax.net.ssl.X509TrustManager;
11+
import java.io.File;
12+
import java.io.FileOutputStream;
1113
import java.io.IOException;
1214
import java.net.InetSocketAddress;
1315
import java.net.Proxy;
@@ -16,10 +18,12 @@
1618
import java.net.http.HttpClient;
1719
import java.net.http.HttpRequest;
1820
import java.net.http.HttpResponse;
21+
import java.nio.file.Files;
22+
import java.nio.file.attribute.FileTime;
1923
import java.security.cert.X509Certificate;
24+
import java.text.SimpleDateFormat;
2025
import java.time.Duration;
21-
import java.util.Arrays;
22-
import java.util.List;
26+
import java.util.*;
2327

2428
public class HttpTiny extends PerlModuleBase {
2529

@@ -32,7 +36,8 @@ public static void initialize() {
3236
httpTiny.initializeExporter();
3337
httpTiny.defineExport("EXPORT", "new", "get", "post", "request");
3438
try {
35-
httpTiny.registerMethod("request", "$$");
39+
httpTiny.registerMethod("request", null);
40+
httpTiny.registerMethod("mirror", null);
3641
} catch (NoSuchMethodException e) {
3742
System.err.println("Warning: Missing HttpTiny method: " + e.getMessage());
3843
}
@@ -188,4 +193,60 @@ private static boolean shouldNotProxy(String host, String noProxy) {
188193
.toList();
189194
return noProxyList.stream().anyMatch(host::endsWith);
190195
}
196+
197+
public static RuntimeList mirror(RuntimeArray args, int ctx) throws Exception {
198+
if (args.size() < 3) {
199+
throw new IllegalStateException("Bad number of arguments for HTTP::Tiny->mirror");
200+
}
201+
202+
RuntimeScalar self = args.get(0);
203+
String url = args.get(1).toString();
204+
String filePath = args.get(2).toString();
205+
RuntimeHash options = args.size() > 3 ? args.get(3).hashDeref() : new RuntimeHash();
206+
207+
File file = new File(filePath);
208+
if (file.exists()) {
209+
// Set If-Modified-Since header
210+
long lastModified = file.lastModified();
211+
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
212+
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
213+
String ifModifiedSince = dateFormat.format(new Date(lastModified));
214+
215+
RuntimeHash headers = options.exists("headers").getBoolean()
216+
? options.get("headers").hashDeref() : new RuntimeHash();
217+
headers.put("If-Modified-Since", new RuntimeScalar(ifModifiedSince));
218+
options.put("headers", headers.createReference());
219+
}
220+
221+
// Perform the request
222+
RuntimeArray requestArgs = new RuntimeArray();
223+
requestArgs.push(self);
224+
requestArgs.push(new RuntimeScalar("GET"));
225+
requestArgs.push(new RuntimeScalar(url));
226+
requestArgs.push(options.createReference());
227+
228+
RuntimeList response = request(requestArgs, ctx);
229+
RuntimeHash responseHash = ((RuntimeScalar) response.elements.get(0)).hashDeref();
230+
231+
// Check if the request was successful or not modified
232+
boolean success = responseHash.get("success").getBoolean() || responseHash.get("status").getLong() == 304;
233+
if (success && responseHash.get("status").getLong() != 304) {
234+
// Write response content to file
235+
try (FileOutputStream fos = new FileOutputStream(file)) {
236+
fos.write(responseHash.get("content").toString().getBytes());
237+
}
238+
239+
// Update file modification time if Last-Modified header is present
240+
RuntimeHash headers = responseHash.get("headers").hashDeref();
241+
if (headers.exists("last-modified").getBoolean()) {
242+
String lastModifiedStr = headers.get("last-modified").toString();
243+
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
244+
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
245+
Date lastModifiedDate = dateFormat.parse(lastModifiedStr);
246+
Files.setLastModifiedTime(file.toPath(), FileTime.fromMillis(lastModifiedDate.getTime()));
247+
}
248+
}
249+
250+
return response;
251+
}
191252
}

src/main/perl/lib/HTTP/Tiny.pm

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -302,46 +302,7 @@ sub post_form {
302302
#pod
303303
#pod =cut
304304

305-
sub mirror {
306-
die "Not implemented: mirror()";
307-
# my ($self, $url, $file, $args) = @_;
308-
# @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
309-
# or _croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n");
310-
311-
# if ( exists $args->{headers} ) {
312-
# my $headers = {};
313-
# while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
314-
# $headers->{lc $key} = $value;
315-
# }
316-
# $args->{headers} = $headers;
317-
# }
318-
319-
# if ( -e $file and my $mtime = (stat($file))[9] ) {
320-
# $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
321-
# }
322-
# my $tempfile = $file . int(rand(2**31));
323-
324-
# require Fcntl;
325-
# sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY()
326-
# or _croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/);
327-
# binmode $fh;
328-
# $args->{data_callback} = sub { print {$fh} $_[0] };
329-
# my $response = $self->request('GET', $url, $args);
330-
# close $fh
331-
# or _croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/);
332-
333-
# if ( $response->{success} ) {
334-
# rename $tempfile, $file
335-
# or _croak(qq/Error replacing $file with $tempfile: $!\n/);
336-
# my $lm = $response->{headers}{'last-modified'};
337-
# if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
338-
# utime $mtime, $mtime, $file;
339-
# }
340-
# }
341-
# $response->{success} ||= $response->{status} eq '304';
342-
# unlink $tempfile;
343-
# return $response;
344-
}
305+
# mirror() is implemented in Java
345306

346307
#pod =method request
347308
#pod

0 commit comments

Comments
 (0)