Tại sao tên máy chủ được khai báo không hợp lệ khi tạo URI


17

Chạy mã này với JDK 1.8:

try {
    System.out.println( new URI(null, null, "5-12-145-35_s-81", 443, null, null, null));
} catch (URISyntaxException e) {
    e.printStackTrace();
}

dẫn đến lỗi này: java.net.URISyntaxException: Illegal character in hostname at index 13: //5-12-145-35_s-81:443

Lỗi này xuất phát từ đâu, xem xét tất cả các ký tự tên máy chủ có vẻ hợp pháp, theo Loại ký tự URI ?


Nếu tôi sử dụng các URL này: //5-12-145-35_s-81:443hoặc /5-12-145-35_s-81:443lỗi đã biến mất.


Từ các bình luận, tôi hiểu rằng, theo RFC-2396 , tên máy chủ không thể chứa bất kỳ ký tự gạch dưới nào.

Câu hỏi vẫn còn đó là tại sao tên máy chủ bắt đầu bằng dấu gạch chéo hoặc dấu gạch chéo kép được phép chứa dấu gạch dưới?


1
@ernest_k Lược đồ không được cung cấp, nó không có giá trị.
Eugen Covaci

nếu bạn vẫn muốn _ trong url @ fg78nc cách giải quyết sẽ phù hợp với bạn. Không sử dụng / vì tên máy chủ sẽ không hợp lệ và sẽ không tạo trường
salesh

3
Xem RFC-2396 phần 3.2.2. Tên máy chủ trong URI chỉ có thể là một hoặc nhiều nhóm chữ và số + -, được phân tách bằng dấu chấm
Mark Rotteveel

@MarkRotteveel java.net.URI không cập nhật với thông số kỹ thuật mới nhất
fg78nc

@ fg78nc Mặc dù RFC-3986 làm thư giãn nó, nó vẫn đề cập rằng "Tên đã đăng ký nhằm tìm kiếm trong DNS sử dụng cú pháp được xác định trong Mục 3.5 của [RFC1034] và Mục 2.1 của [RFC1123]." và về cơ bản đó là cú pháp của RFC-2396 phần 3.2.2.
Đánh dấu Rotteveel

Câu trả lời:


8

Tên máy chủ phải khớp với cú pháp sau:

hostname      = domainlabel [ "." ] | 1*( domainlabel "." ) toplabel [ "." ]
domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
toplabel      = alpha | alpha *( alphanum | "-" ) alphanum

Như bạn có thể thấy, chỉ .-được cho phép,_ là không.


Sau đó, bạn nói rằng //5-12-145-35_s-81:443được cho phép, và nó là, nhưng không phải cho tên máy chủ .

Để xem làm thế nào mà chảo ra:

URI uriBadHost = URI.create("//5-12-145-35_s-81:443");
System.out.println("uri = " + uriBadHost);
System.out.println("  authority = " + uriBadHost.getAuthority());
System.out.println("  host = " + uriBadHost.getHost());
System.out.println("  port = " + uriBadHost.getPort());
URI uriGoodHost = URI.create("//example.com:443");
System.out.println("uri = " + uriGoodHost);
System.out.println("  authority = " + uriGoodHost.getAuthority());
System.out.println("  host = " + uriGoodHost.getHost());
System.out.println("  port = " + uriGoodHost.getPort());

Đầu ra

uri = //5-12-145-35_s-81:443
  authority = 5-12-145-35_s-81:443
  host = null
  port = -1
uri = //example.com:443
  authority = example.com:443
  host = example.com
  port = 443

Như bạn có thể thấy, khi authoritycó tên máy chủ hợp lệ, hostport được phân tích cú pháp, nhưng khi không hợp lệ, authoritynó được coi là văn bản dạng tự do và không được phân tích cú pháp nữa.


CẬP NHẬT

Từ bình luận:

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))đầu ra: /// 5-12-145-35_s-81: 443. Tôi đang đặt nó làm tên máy chủ

Các URI tạo bạn đang gọi là một phương thức tiện lợi và đơn giản nó xây dựng một chuỗi URI đầy đủ và sau đó phân tích cú pháp đó.

Đi qua "5-12-145-35_s-81", 443trở thành //5-12-145-35_s-81:443.
Vượt qua "/5-12-145-35_s-81", 443trở thành///5-12-145-35_s-81:443 .

Đầu tiên, nó là một máy chủcổng , và không thể phân tích cú pháp.
Trong phần thứ hai, phần thẩm quyền là trống rỗng, và /5-12-145-35_s-81:443là một con đường .

URI uri1 = new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null);
System.out.println("uri = " + uri1);
System.out.println("  authority = " + uri1.getAuthority());
System.out.println("  host = " + uri1.getHost());
System.out.println("  port = " + uri1.getPort());
System.out.println("  path = " + uri1.getPath());

Đầu ra

uri = ///5-12-145-35_s-81:443
  authority = null
  host = null
  port = -1
  path = /5-12-145-35_s-81:443

Bây giờ tôi hiểu, nhưng tại sao, giả sử /a_b, được cho phép. Sự khác biệt duy nhất là cái này là tuyệt đối, không tương đối
Eugen Covaci

System.out.println( new URI(null, null, "/5-12-145-35_s-81", 443, null, null, null))đầu ra : ///5-12-145-35_s-81:443. Tôi đang đặt nó làm tên máy chủ.
Eugen Covaci

Hành vi này (khi tên máy chủ là tuyệt đối) là lạ, để nói rằng ít nhất. Hàm tạo của URI đang cung cấp tên máy chủ và cổng và URI kết quả không có một, chỉ có một đường dẫn.
Eugen Covaci

5

Lỗi không phải ở Java mà là đặt tên máy chủ, vì dấu gạch dưới không phải là ký tự hợp lệ trong tên máy chủ. Mặc dù được sử dụng rộng rãi không chính xác, Java từ chối xử lý các tên máy chủ đó


Điều này /5-12-145-35_s-81:443là hợp pháp.
Eugen Covaci

2

Dấu gạch dưới không được hỗ trợ trong URI.

Mặc dù tên máy chủ có thể không chứa các ký tự khác, chẳng hạn như ký tự gạch dưới (_), các tên DNS khác có thể chứa dấu gạch dưới. [5] [6] Hạn chế này đã được RFC 2181, Phần 11. Các hệ thống như DomainKeys và hồ sơ dịch vụ sử dụng dấu gạch dưới như một phương tiện để đảm bảo rằng ký tự đặc biệt của chúng không bị nhầm lẫn với tên máy chủ. Ví dụ: _http._sctp.www.example.com chỉ định một con trỏ dịch vụ cho máy chủ lưu trữ web có khả năng SCTP (www) trong ví dụ miền.com. Mặc dù tiêu chuẩn, Chrome, Firefox, Internet Explorer, Edge và Safari cho phép gạch dưới tên máy chủ, mặc dù cookie trong IE không hoạt động chính xác nếu bất kỳ phần nào của tên máy chủ chứa ký tự gạch dưới

Wikipedia

Từ Javadocs:

URI công khai (Chuỗi str) ném URISyntaxException Ném: URISyntaxException - Nếu chuỗi đã cho vi phạm RFC 2396, như được tăng thêm bởi các độ lệch trên

Javadocs

(Hacky) Giải pháp:

    URI url = URI.create("https://5-12-145-35_s-8:8080");

    System.out.println(url.getHost()) // null

    if (url.getHost() == null) {
        final Field hostField = URI.class.getDeclaredField("host");
        hostField.setAccessible(true);
        hostField.set(url, "5-12-145-35_s-81");
    }
    System.out.println(url.getHost()); // 5-12-145-35_s-81

Điều này đã được báo cáo là - lỗi JDK


1
Wow, đó là một giải pháp hacky. Bạn có thể nói rằng điều này có thể phá vỡ trong tương lai, vì nó giả định nội bộ về một lớp bên trong và sử dụng sự phản chiếu để truy cập trực tiếp vào nó. Vì vậy, việc triển khai có thể thay đổi với bất kỳ bản phát hành Java nào, trong trường hợp này có thể bị hỏng. +1 để cung cấp một giải pháp mặc dù.
Zabuzard

Tôi muốn đưa ra cách giải quyết này mà tôi đã không làm, vấn đề với những điều này chỉ là những gì Zabuza đề cập. + Nếu chúng ta bắt đầu tuân theo quy tắc, mọi thứ sẽ bắt đầu từ từ sụp đổ. Có một lý do tốt tại sao điều này không hoạt động ở nơi đầu tiên.
salesh

@salesh Và lý do chính đáng đó là gì?
fg78nc

"Các hệ thống như DomainKeys và hồ sơ dịch vụ sử dụng dấu gạch dưới như một phương tiện để đảm bảo rằng ký tự đặc biệt của chúng không bị nhầm lẫn với tên máy chủ." wikipedia và có câu trả lời tốt ở đây quora
salesh

1
Nếu bạn làm điều đó bạn sẽ nhận được nullnhư là máy chủ lưu trữ.
fg78nc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.