mirror of https://github.com/adamdruppe/arsd.git
catchup
This commit is contained in:
parent
046d55e880
commit
d3506d8b00
|
@ -113,7 +113,7 @@ bool processTar(
|
||||||
if(*bytesRemainingOnCurrentFile) {
|
if(*bytesRemainingOnCurrentFile) {
|
||||||
bool isNew = *bytesRemainingOnCurrentFile == header.size();
|
bool isNew = *bytesRemainingOnCurrentFile == header.size();
|
||||||
if(*bytesRemainingOnCurrentFile < 512) {
|
if(*bytesRemainingOnCurrentFile < 512) {
|
||||||
handleData(header, isNew, true, dataBuffer[0 .. *bytesRemainingOnCurrentFile]);
|
handleData(header, isNew, true, dataBuffer[0 .. cast(size_t) *bytesRemainingOnCurrentFile]);
|
||||||
*bytesRemainingOnCurrentFile = 0;
|
*bytesRemainingOnCurrentFile = 0;
|
||||||
} else {
|
} else {
|
||||||
handleData(header, isNew, false, dataBuffer[]);
|
handleData(header, isNew, false, dataBuffer[]);
|
||||||
|
|
6
cgi.d
6
cgi.d
|
@ -9069,11 +9069,11 @@ bool apiDispatcher()(Cgi cgi) {
|
||||||
}
|
}
|
||||||
+/
|
+/
|
||||||
/*
|
/*
|
||||||
Copyright: Adam D. Ruppe, 2008 - 2019
|
Copyright: Adam D. Ruppe, 2008 - 2020
|
||||||
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
|
License: [http://www.boost.org/LICENSE_1_0.txt|Boost License 1.0].
|
||||||
Authors: Adam D. Ruppe
|
Authors: Adam D. Ruppe
|
||||||
|
|
||||||
Copyright Adam D. Ruppe 2008 - 2019.
|
Copyright Adam D. Ruppe 2008 - 2020.
|
||||||
Distributed under the Boost Software License, Version 1.0.
|
Distributed under the Boost Software License, Version 1.0.
|
||||||
(See accompanying file LICENSE_1_0.txt or copy at
|
(See accompanying file LICENSE_1_0.txt or copy at
|
||||||
http://www.boost.org/LICENSE_1_0.txt)
|
http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
71
dom.d
71
dom.d
|
@ -33,6 +33,21 @@
|
||||||
|
|
||||||
If you want it to stand alone, just always use the `Document.parseUtf8`
|
If you want it to stand alone, just always use the `Document.parseUtf8`
|
||||||
function or the constructor that takes a string.
|
function or the constructor that takes a string.
|
||||||
|
|
||||||
|
Symbol_groups:
|
||||||
|
|
||||||
|
core_functionality =
|
||||||
|
|
||||||
|
These members provide core functionality. The members on these classes
|
||||||
|
will provide most your direct interaction.
|
||||||
|
|
||||||
|
bonus_functionality =
|
||||||
|
|
||||||
|
These provide additional functionality for special use cases.
|
||||||
|
|
||||||
|
implementations =
|
||||||
|
|
||||||
|
These provide implementations of other functionality.
|
||||||
+/
|
+/
|
||||||
module arsd.dom;
|
module arsd.dom;
|
||||||
|
|
||||||
|
@ -78,6 +93,7 @@ bool isConvenientAttribute(string name) {
|
||||||
|
|
||||||
|
|
||||||
/// The main document interface, including a html parser.
|
/// The main document interface, including a html parser.
|
||||||
|
/// Group: core_functionality
|
||||||
class Document : FileResource {
|
class Document : FileResource {
|
||||||
/// Convenience method for web scraping. Requires [arsd.http2] to be
|
/// Convenience method for web scraping. Requires [arsd.http2] to be
|
||||||
/// included in the build as well as [arsd.characterencodings].
|
/// included in the build as well as [arsd.characterencodings].
|
||||||
|
@ -1423,6 +1439,7 @@ class Document : FileResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This represents almost everything in the DOM.
|
/// This represents almost everything in the DOM.
|
||||||
|
/// Group: core_functionality
|
||||||
class Element {
|
class Element {
|
||||||
/// Returns a collection of elements by selector.
|
/// Returns a collection of elements by selector.
|
||||||
/// See: [Document.opIndex]
|
/// See: [Document.opIndex]
|
||||||
|
@ -3474,6 +3491,7 @@ class Element {
|
||||||
|
|
||||||
// FIXME: since Document loosens the input requirements, it should probably be the sub class...
|
// FIXME: since Document loosens the input requirements, it should probably be the sub class...
|
||||||
/// Specializes Document for handling generic XML. (always uses strict mode, uses xml mime type and file header)
|
/// Specializes Document for handling generic XML. (always uses strict mode, uses xml mime type and file header)
|
||||||
|
/// Group: core_functionality
|
||||||
class XmlDocument : Document {
|
class XmlDocument : Document {
|
||||||
this(string data) {
|
this(string data) {
|
||||||
contentType = "text/xml; charset=utf-8";
|
contentType = "text/xml; charset=utf-8";
|
||||||
|
@ -3491,6 +3509,7 @@ import std.string;
|
||||||
/* domconvenience follows { */
|
/* domconvenience follows { */
|
||||||
|
|
||||||
/// finds comments that match the given txt. Case insensitive, strips whitespace.
|
/// finds comments that match the given txt. Case insensitive, strips whitespace.
|
||||||
|
/// Group: core_functionality
|
||||||
Element[] findComments(Document document, string txt) {
|
Element[] findComments(Document document, string txt) {
|
||||||
return findComments(document.root, txt);
|
return findComments(document.root, txt);
|
||||||
}
|
}
|
||||||
|
@ -3510,6 +3529,7 @@ Element[] findComments(Element element, string txt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An option type that propagates null. See: [Element.optionSelector]
|
/// An option type that propagates null. See: [Element.optionSelector]
|
||||||
|
/// Group: implementations
|
||||||
struct MaybeNullElement(SomeElementType) {
|
struct MaybeNullElement(SomeElementType) {
|
||||||
this(SomeElementType ele) {
|
this(SomeElementType ele) {
|
||||||
this.element = ele;
|
this.element = ele;
|
||||||
|
@ -3543,6 +3563,7 @@ struct MaybeNullElement(SomeElementType) {
|
||||||
/++
|
/++
|
||||||
A collection of elements which forwards methods to the children.
|
A collection of elements which forwards methods to the children.
|
||||||
+/
|
+/
|
||||||
|
/// Group: implementations
|
||||||
struct ElementCollection {
|
struct ElementCollection {
|
||||||
///
|
///
|
||||||
this(Element e) {
|
this(Element e) {
|
||||||
|
@ -3641,6 +3662,7 @@ struct ElementCollection {
|
||||||
|
|
||||||
|
|
||||||
/// this puts in operators and opDispatch to handle string indexes and properties, forwarding to get and set functions.
|
/// this puts in operators and opDispatch to handle string indexes and properties, forwarding to get and set functions.
|
||||||
|
/// Group: implementations
|
||||||
mixin template JavascriptStyleDispatch() {
|
mixin template JavascriptStyleDispatch() {
|
||||||
///
|
///
|
||||||
string opDispatch(string name)(string v = null) if(name != "popFront") { // popFront will make this look like a range. Do not want.
|
string opDispatch(string name)(string v = null) if(name != "popFront") { // popFront will make this look like a range. Do not want.
|
||||||
|
@ -3668,6 +3690,7 @@ mixin template JavascriptStyleDispatch() {
|
||||||
/// A proxy object to do the Element class' dataset property. See Element.dataset for more info.
|
/// A proxy object to do the Element class' dataset property. See Element.dataset for more info.
|
||||||
///
|
///
|
||||||
/// Do not create this object directly.
|
/// Do not create this object directly.
|
||||||
|
/// Group: implementations
|
||||||
struct DataSet {
|
struct DataSet {
|
||||||
///
|
///
|
||||||
this(Element e) {
|
this(Element e) {
|
||||||
|
@ -3691,6 +3714,7 @@ struct DataSet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Proxy object for attributes which will replace the main opDispatch eventually
|
/// Proxy object for attributes which will replace the main opDispatch eventually
|
||||||
|
/// Group: implementations
|
||||||
struct AttributeSet {
|
struct AttributeSet {
|
||||||
///
|
///
|
||||||
this(Element e) {
|
this(Element e) {
|
||||||
|
@ -3718,6 +3742,7 @@ struct AttributeSet {
|
||||||
/// for style, i want to be able to set it with a string like a plain attribute,
|
/// for style, i want to be able to set it with a string like a plain attribute,
|
||||||
/// but also be able to do properties Javascript style.
|
/// but also be able to do properties Javascript style.
|
||||||
|
|
||||||
|
/// Group: implementations
|
||||||
struct ElementStyle {
|
struct ElementStyle {
|
||||||
this(Element parent) {
|
this(Element parent) {
|
||||||
_element = parent;
|
_element = parent;
|
||||||
|
@ -3874,6 +3899,7 @@ import std.range;
|
||||||
Document implements this interface with type = text/html (see Document.contentType for more info)
|
Document implements this interface with type = text/html (see Document.contentType for more info)
|
||||||
and data = document.toString, so you can return Documents anywhere web.d expects FileResources.
|
and data = document.toString, so you can return Documents anywhere web.d expects FileResources.
|
||||||
+/
|
+/
|
||||||
|
/// Group: bonus_functionality
|
||||||
interface FileResource {
|
interface FileResource {
|
||||||
/// the content-type of the file. e.g. "text/html; charset=utf-8" or "image/png"
|
/// the content-type of the file. e.g. "text/html; charset=utf-8" or "image/png"
|
||||||
@property string contentType() const;
|
@property string contentType() const;
|
||||||
|
@ -3885,10 +3911,12 @@ interface FileResource {
|
||||||
|
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: bonus_functionality
|
||||||
enum NodeType { Text = 3 }
|
enum NodeType { Text = 3 }
|
||||||
|
|
||||||
|
|
||||||
/// You can use this to do an easy null check or a dynamic cast+null check on any element.
|
/// You can use this to do an easy null check or a dynamic cast+null check on any element.
|
||||||
|
/// Group: core_functionality
|
||||||
T require(T = Element, string file = __FILE__, int line = __LINE__)(Element e) if(is(T : Element))
|
T require(T = Element, string file = __FILE__, int line = __LINE__)(Element e) if(is(T : Element))
|
||||||
in {}
|
in {}
|
||||||
out(ret) { assert(ret !is null); }
|
out(ret) { assert(ret !is null); }
|
||||||
|
@ -3901,6 +3929,7 @@ body {
|
||||||
|
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: core_functionality
|
||||||
class DocumentFragment : Element {
|
class DocumentFragment : Element {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument) {
|
this(Document _parentDocument) {
|
||||||
|
@ -3951,6 +3980,7 @@ class DocumentFragment : Element {
|
||||||
///
|
///
|
||||||
/// The output parameter can be given to append to an existing buffer. You don't have to
|
/// The output parameter can be given to append to an existing buffer. You don't have to
|
||||||
/// pass one; regardless, the return value will be usable for you, with just the data encoded.
|
/// pass one; regardless, the return value will be usable for you, with just the data encoded.
|
||||||
|
/// Group: core_functionality
|
||||||
string htmlEntitiesEncode(string data, Appender!string output = appender!string(), bool encodeNonAscii = true) {
|
string htmlEntitiesEncode(string data, Appender!string output = appender!string(), bool encodeNonAscii = true) {
|
||||||
// if there's no entities, we can save a lot of time by not bothering with the
|
// if there's no entities, we can save a lot of time by not bothering with the
|
||||||
// decoding loop. This check cuts the net toString time by better than half in my test.
|
// decoding loop. This check cuts the net toString time by better than half in my test.
|
||||||
|
@ -4003,11 +4033,13 @@ string htmlEntitiesEncode(string data, Appender!string output = appender!string(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An alias for htmlEntitiesEncode; it works for xml too
|
/// An alias for htmlEntitiesEncode; it works for xml too
|
||||||
|
/// Group: core_functionality
|
||||||
string xmlEntitiesEncode(string data) {
|
string xmlEntitiesEncode(string data) {
|
||||||
return htmlEntitiesEncode(data);
|
return htmlEntitiesEncode(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This helper function is used for decoding html entities. It has a hard-coded list of entities and characters.
|
/// This helper function is used for decoding html entities. It has a hard-coded list of entities and characters.
|
||||||
|
/// Group: core_functionality
|
||||||
dchar parseEntity(in dchar[] entity) {
|
dchar parseEntity(in dchar[] entity) {
|
||||||
switch(entity[1..$-1]) {
|
switch(entity[1..$-1]) {
|
||||||
case "quot":
|
case "quot":
|
||||||
|
@ -5505,6 +5537,7 @@ import std.stdio;
|
||||||
/// This takes a string of raw HTML and decodes the entities into a nice D utf-8 string.
|
/// This takes a string of raw HTML and decodes the entities into a nice D utf-8 string.
|
||||||
/// By default, it uses loose mode - it will try to return a useful string from garbage input too.
|
/// By default, it uses loose mode - it will try to return a useful string from garbage input too.
|
||||||
/// Set the second parameter to true if you'd prefer it to strictly throw exceptions on garbage input.
|
/// Set the second parameter to true if you'd prefer it to strictly throw exceptions on garbage input.
|
||||||
|
/// Group: core_functionality
|
||||||
string htmlEntitiesDecode(string data, bool strict = false) {
|
string htmlEntitiesDecode(string data, bool strict = false) {
|
||||||
// this check makes a *big* difference; about a 50% improvement of parse speed on my test.
|
// this check makes a *big* difference; about a 50% improvement of parse speed on my test.
|
||||||
if(data.indexOf("&") == -1) // all html entities begin with &
|
if(data.indexOf("&") == -1) // all html entities begin with &
|
||||||
|
@ -5585,6 +5618,7 @@ string htmlEntitiesDecode(string data, bool strict = false) {
|
||||||
return cast(string) a; // assumeUnique is actually kinda slow, lol
|
return cast(string) a; // assumeUnique is actually kinda slow, lol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Group: implementations
|
||||||
abstract class SpecialElement : Element {
|
abstract class SpecialElement : Element {
|
||||||
this(Document _parentDocument) {
|
this(Document _parentDocument) {
|
||||||
super(_parentDocument);
|
super(_parentDocument);
|
||||||
|
@ -5602,6 +5636,7 @@ abstract class SpecialElement : Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class RawSource : SpecialElement {
|
class RawSource : SpecialElement {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument, string s) {
|
this(Document _parentDocument, string s) {
|
||||||
|
@ -5634,6 +5669,7 @@ class RawSource : SpecialElement {
|
||||||
string source;
|
string source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Group: implementations
|
||||||
abstract class ServerSideCode : SpecialElement {
|
abstract class ServerSideCode : SpecialElement {
|
||||||
this(Document _parentDocument, string type) {
|
this(Document _parentDocument, string type) {
|
||||||
super(_parentDocument);
|
super(_parentDocument);
|
||||||
|
@ -5663,6 +5699,7 @@ abstract class ServerSideCode : SpecialElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class PhpCode : ServerSideCode {
|
class PhpCode : ServerSideCode {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument, string s) {
|
this(Document _parentDocument, string s) {
|
||||||
|
@ -5676,6 +5713,7 @@ class PhpCode : ServerSideCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class AspCode : ServerSideCode {
|
class AspCode : ServerSideCode {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument, string s) {
|
this(Document _parentDocument, string s) {
|
||||||
|
@ -5689,6 +5727,7 @@ class AspCode : ServerSideCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class BangInstruction : SpecialElement {
|
class BangInstruction : SpecialElement {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument, string s) {
|
this(Document _parentDocument, string s) {
|
||||||
|
@ -5728,6 +5767,7 @@ class BangInstruction : SpecialElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class QuestionInstruction : SpecialElement {
|
class QuestionInstruction : SpecialElement {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument, string s) {
|
this(Document _parentDocument, string s) {
|
||||||
|
@ -5768,6 +5808,7 @@ class QuestionInstruction : SpecialElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class HtmlComment : SpecialElement {
|
class HtmlComment : SpecialElement {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument, string s) {
|
this(Document _parentDocument, string s) {
|
||||||
|
@ -5811,6 +5852,7 @@ class HtmlComment : SpecialElement {
|
||||||
|
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class TextNode : Element {
|
class TextNode : Element {
|
||||||
public:
|
public:
|
||||||
///.
|
///.
|
||||||
|
@ -5926,6 +5968,7 @@ class TextNode : Element {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class Link : Element {
|
class Link : Element {
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
@ -6064,6 +6107,7 @@ class Link : Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class Form : Element {
|
class Form : Element {
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
@ -6314,6 +6358,7 @@ class Form : Element {
|
||||||
import std.conv;
|
import std.conv;
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class Table : Element {
|
class Table : Element {
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
@ -6553,6 +6598,7 @@ class Table : Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a table row element - a <tr>
|
/// Represents a table row element - a <tr>
|
||||||
|
/// Group: implementations
|
||||||
class TableRow : Element {
|
class TableRow : Element {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument) {
|
this(Document _parentDocument) {
|
||||||
|
@ -6565,6 +6611,7 @@ class TableRow : Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents anything that can be a table cell - <td> or <th> html.
|
/// Represents anything that can be a table cell - <td> or <th> html.
|
||||||
|
/// Group: implementations
|
||||||
class TableCell : Element {
|
class TableCell : Element {
|
||||||
///.
|
///.
|
||||||
this(Document _parentDocument, string _tagName) {
|
this(Document _parentDocument, string _tagName) {
|
||||||
|
@ -6601,6 +6648,7 @@ class TableCell : Element {
|
||||||
|
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
/// Group: implementations
|
||||||
class MarkupException : Exception {
|
class MarkupException : Exception {
|
||||||
|
|
||||||
///.
|
///.
|
||||||
|
@ -6610,6 +6658,7 @@ class MarkupException : Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is used when you are using one of the require variants of navigation, and no matching element can be found in the tree.
|
/// This is used when you are using one of the require variants of navigation, and no matching element can be found in the tree.
|
||||||
|
/// Group: implementations
|
||||||
class ElementNotFoundException : Exception {
|
class ElementNotFoundException : Exception {
|
||||||
|
|
||||||
/// type == kind of element you were looking for and search == a selector describing the search.
|
/// type == kind of element you were looking for and search == a selector describing the search.
|
||||||
|
@ -6624,6 +6673,7 @@ class ElementNotFoundException : Exception {
|
||||||
/// The html struct is used to differentiate between regular text nodes and html in certain functions
|
/// The html struct is used to differentiate between regular text nodes and html in certain functions
|
||||||
///
|
///
|
||||||
/// Easiest way to construct it is like this: `auto html = Html("<p>hello</p>");`
|
/// Easiest way to construct it is like this: `auto html = Html("<p>hello</p>");`
|
||||||
|
/// Group: core_functionality
|
||||||
struct Html {
|
struct Html {
|
||||||
/// This string holds the actual html. Use it to retrieve the contents.
|
/// This string holds the actual html. Use it to retrieve the contents.
|
||||||
string source;
|
string source;
|
||||||
|
@ -7294,19 +7344,24 @@ int intFromHex(string hex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Represents a parsed CSS selector.
|
Represents a parsed CSS selector. You never have to use this directly, but you can if you know it is going to be reused a lot to avoid a bit of repeat parsing.
|
||||||
|
|
||||||
See_Also:
|
See_Also:
|
||||||
[Element.querySelector]
|
$(LIST
|
||||||
[Element.querySelectorAll]
|
* [Element.querySelector]
|
||||||
[Document.querySelector]
|
* [Element.querySelectorAll]
|
||||||
[Document.querySelectorAll]
|
* [Element.matches]
|
||||||
|
* [Element.closest]
|
||||||
|
* [Document.querySelector]
|
||||||
|
* [Document.querySelectorAll]
|
||||||
|
)
|
||||||
+/
|
+/
|
||||||
|
/// Group: core_functionality
|
||||||
struct Selector {
|
struct Selector {
|
||||||
SelectorComponent[] components;
|
SelectorComponent[] components;
|
||||||
string original;
|
string original;
|
||||||
/++
|
/++
|
||||||
Parses the selector string and returns the usable structure.
|
Parses the selector string and constructs the usable structure.
|
||||||
+/
|
+/
|
||||||
this(string cssSelector) {
|
this(string cssSelector) {
|
||||||
components = parseSelectorString(cssSelector);
|
components = parseSelectorString(cssSelector);
|
||||||
|
@ -8709,11 +8764,11 @@ unittest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright: Adam D. Ruppe, 2010 - 2019
|
Copyright: Adam D. Ruppe, 2010 - 2020
|
||||||
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
|
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
|
||||||
Authors: Adam D. Ruppe, with contributions by Nick Sabalausky, Trass3r, and ketmar among others
|
Authors: Adam D. Ruppe, with contributions by Nick Sabalausky, Trass3r, and ketmar among others
|
||||||
|
|
||||||
Copyright Adam D. Ruppe 2010-2019.
|
Copyright Adam D. Ruppe 2010-2020.
|
||||||
Distributed under the Boost Software License, Version 1.0.
|
Distributed under the Boost Software License, Version 1.0.
|
||||||
(See accompanying file LICENSE_1_0.txt or copy at
|
(See accompanying file LICENSE_1_0.txt or copy at
|
||||||
http://www.boost.org/LICENSE_1_0.txt)
|
http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
2
dub.json
2
dub.json
|
@ -3,7 +3,7 @@
|
||||||
"targetType": "library",
|
"targetType": "library",
|
||||||
"importPaths": ["."],
|
"importPaths": ["."],
|
||||||
"sourceFiles": ["package.d"],
|
"sourceFiles": ["package.d"],
|
||||||
"description": "Subpackage collection for web, database, terminal ui, gui, scripting, and more.",
|
"description": "Subpackage collection for web, database, terminal ui, gui, scripting, and more with a commitment to long-term compatibility and stability.",
|
||||||
"authors": ["Adam D. Ruppe"],
|
"authors": ["Adam D. Ruppe"],
|
||||||
"license":"BSL-1.0",
|
"license":"BSL-1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
338
http2.d
338
http2.d
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2013-2019, Adam D. Ruppe.
|
// Copyright 2013-2020, Adam D. Ruppe.
|
||||||
/++
|
/++
|
||||||
This is version 2 of my http/1.1 client implementation.
|
This is version 2 of my http/1.1 client implementation.
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ debug(arsd_http2_verbose) debug=arsd_http2;
|
||||||
|
|
||||||
debug(arsd_http2) import std.stdio : writeln;
|
debug(arsd_http2) import std.stdio : writeln;
|
||||||
|
|
||||||
|
version=arsd_http_internal_implementation;
|
||||||
|
|
||||||
version(without_openssl) {}
|
version(without_openssl) {}
|
||||||
else {
|
else {
|
||||||
version=use_openssl;
|
version=use_openssl;
|
||||||
|
@ -28,6 +30,12 @@ version(older_openssl) {} else
|
||||||
version=newer_openssl;
|
version=newer_openssl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(arsd_http_winhttp_implementation) {
|
||||||
|
pragma(lib, "winhttp")
|
||||||
|
import core.sys.windows.winhttp;
|
||||||
|
// FIXME: alter the dub package file too
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -605,6 +613,176 @@ class HttpRequest {
|
||||||
/// Automatically follow a redirection?
|
/// Automatically follow a redirection?
|
||||||
bool followLocation = false;
|
bool followLocation = false;
|
||||||
|
|
||||||
|
this() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
this(Uri where, HttpVerb method) {
|
||||||
|
populateFromInfo(where, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Final url after any redirections
|
||||||
|
string finalUrl;
|
||||||
|
|
||||||
|
void populateFromInfo(Uri where, HttpVerb method) {
|
||||||
|
auto parts = where;
|
||||||
|
finalUrl = where.toString();
|
||||||
|
requestParameters.method = method;
|
||||||
|
requestParameters.host = parts.host;
|
||||||
|
requestParameters.port = cast(ushort) parts.port;
|
||||||
|
requestParameters.ssl = parts.scheme == "https";
|
||||||
|
if(parts.port == 0)
|
||||||
|
requestParameters.port = requestParameters.ssl ? 443 : 80;
|
||||||
|
requestParameters.uri = parts.path.length ? parts.path : "/";
|
||||||
|
if(parts.query.length) {
|
||||||
|
requestParameters.uri ~= "?";
|
||||||
|
requestParameters.uri ~= parts.query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~this() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ubyte[] sendBuffer;
|
||||||
|
|
||||||
|
HttpResponse responseData;
|
||||||
|
private HttpClient parentClient;
|
||||||
|
|
||||||
|
size_t bodyBytesSent;
|
||||||
|
size_t bodyBytesReceived;
|
||||||
|
|
||||||
|
State state_;
|
||||||
|
State state() { return state_; }
|
||||||
|
State state(State s) {
|
||||||
|
assert(state_ != State.complete);
|
||||||
|
return state_ = s;
|
||||||
|
}
|
||||||
|
/// Called when data is received. Check the state to see what data is available.
|
||||||
|
void delegate(HttpRequest) onDataReceived;
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
/// The request has not yet been sent
|
||||||
|
unsent,
|
||||||
|
|
||||||
|
/// The send() method has been called, but no data is
|
||||||
|
/// sent on the socket yet because the connection is busy.
|
||||||
|
pendingAvailableConnection,
|
||||||
|
|
||||||
|
/// The headers are being sent now
|
||||||
|
sendingHeaders,
|
||||||
|
|
||||||
|
/// The body is being sent now
|
||||||
|
sendingBody,
|
||||||
|
|
||||||
|
/// The request has been sent but we haven't received any response yet
|
||||||
|
waitingForResponse,
|
||||||
|
|
||||||
|
/// We have received some data and are currently receiving headers
|
||||||
|
readingHeaders,
|
||||||
|
|
||||||
|
/// All headers are available but we're still waiting on the body
|
||||||
|
readingBody,
|
||||||
|
|
||||||
|
/// The request is complete.
|
||||||
|
complete,
|
||||||
|
|
||||||
|
/// The request is aborted, either by the abort() method, or as a result of the server disconnecting
|
||||||
|
aborted
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends now and waits for the request to finish, returning the response.
|
||||||
|
HttpResponse perform() {
|
||||||
|
send();
|
||||||
|
return waitForCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends the request asynchronously.
|
||||||
|
void send() {
|
||||||
|
sendPrivate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendPrivate(bool advance) {
|
||||||
|
if(state != State.unsent && state != State.aborted)
|
||||||
|
return; // already sent
|
||||||
|
string headers;
|
||||||
|
|
||||||
|
headers ~= to!string(requestParameters.method) ~ " "~requestParameters.uri;
|
||||||
|
if(requestParameters.useHttp11)
|
||||||
|
headers ~= " HTTP/1.1\r\n";
|
||||||
|
else
|
||||||
|
headers ~= " HTTP/1.0\r\n";
|
||||||
|
headers ~= "Host: "~requestParameters.host~"\r\n";
|
||||||
|
if(requestParameters.userAgent.length)
|
||||||
|
headers ~= "User-Agent: "~requestParameters.userAgent~"\r\n";
|
||||||
|
if(requestParameters.contentType.length)
|
||||||
|
headers ~= "Content-Type: "~requestParameters.contentType~"\r\n";
|
||||||
|
if(requestParameters.authorization.length)
|
||||||
|
headers ~= "Authorization: "~requestParameters.authorization~"\r\n";
|
||||||
|
if(requestParameters.bodyData.length)
|
||||||
|
headers ~= "Content-Length: "~to!string(requestParameters.bodyData.length)~"\r\n";
|
||||||
|
if(requestParameters.acceptGzip)
|
||||||
|
headers ~= "Accept-Encoding: gzip\r\n";
|
||||||
|
|
||||||
|
foreach(header; requestParameters.headers)
|
||||||
|
headers ~= header ~ "\r\n";
|
||||||
|
|
||||||
|
headers ~= "\r\n";
|
||||||
|
|
||||||
|
sendBuffer = cast(ubyte[]) headers ~ requestParameters.bodyData;
|
||||||
|
|
||||||
|
// import std.stdio; writeln("******* ", sendBuffer);
|
||||||
|
|
||||||
|
responseData = HttpResponse.init;
|
||||||
|
responseData.requestParameters = requestParameters;
|
||||||
|
bodyBytesSent = 0;
|
||||||
|
bodyBytesReceived = 0;
|
||||||
|
state = State.pendingAvailableConnection;
|
||||||
|
|
||||||
|
bool alreadyPending = false;
|
||||||
|
foreach(req; pending)
|
||||||
|
if(req is this) {
|
||||||
|
alreadyPending = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(!alreadyPending) {
|
||||||
|
pending ~= this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(advance)
|
||||||
|
HttpRequest.advanceConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Waits for the request to finish or timeout, whichever comes first.
|
||||||
|
HttpResponse waitForCompletion() {
|
||||||
|
while(state != State.aborted && state != State.complete) {
|
||||||
|
if(state == State.unsent)
|
||||||
|
send();
|
||||||
|
if(auto err = HttpRequest.advanceConnections())
|
||||||
|
throw new Exception("waitForCompletion got err " ~ to!string(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
return responseData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aborts this request.
|
||||||
|
void abort() {
|
||||||
|
this.state = State.aborted;
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpRequestParameters requestParameters; ///
|
||||||
|
|
||||||
|
version(arsd_http_winhttp_implementation) {
|
||||||
|
public static void resetInternals() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static assert(0, "implementation not finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
version(arsd_http_internal_implementation) {
|
||||||
private static {
|
private static {
|
||||||
// we manage the actual connections. When a request is made on a particular
|
// we manage the actual connections. When a request is made on a particular
|
||||||
// host, we try to reuse connections. We may open more than one connection per
|
// host, we try to reuse connections. We may open more than one connection per
|
||||||
|
@ -1170,165 +1348,7 @@ class HttpRequest {
|
||||||
return stillAlive;
|
return stillAlive;
|
||||||
}
|
}
|
||||||
|
|
||||||
this() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
|
||||||
this(Uri where, HttpVerb method) {
|
|
||||||
populateFromInfo(where, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Final url after any redirections
|
|
||||||
string finalUrl;
|
|
||||||
|
|
||||||
void populateFromInfo(Uri where, HttpVerb method) {
|
|
||||||
auto parts = where;
|
|
||||||
finalUrl = where.toString();
|
|
||||||
requestParameters.method = method;
|
|
||||||
requestParameters.host = parts.host;
|
|
||||||
requestParameters.port = cast(ushort) parts.port;
|
|
||||||
requestParameters.ssl = parts.scheme == "https";
|
|
||||||
if(parts.port == 0)
|
|
||||||
requestParameters.port = requestParameters.ssl ? 443 : 80;
|
|
||||||
requestParameters.uri = parts.path.length ? parts.path : "/";
|
|
||||||
if(parts.query.length) {
|
|
||||||
requestParameters.uri ~= "?";
|
|
||||||
requestParameters.uri ~= parts.query;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~this() {
|
|
||||||
}
|
|
||||||
|
|
||||||
ubyte[] sendBuffer;
|
|
||||||
|
|
||||||
HttpResponse responseData;
|
|
||||||
private HttpClient parentClient;
|
|
||||||
|
|
||||||
size_t bodyBytesSent;
|
|
||||||
size_t bodyBytesReceived;
|
|
||||||
|
|
||||||
State state_;
|
|
||||||
State state() { return state_; }
|
|
||||||
State state(State s) {
|
|
||||||
assert(state_ != State.complete);
|
|
||||||
return state_ = s;
|
|
||||||
}
|
|
||||||
/// Called when data is received. Check the state to see what data is available.
|
|
||||||
void delegate(HttpRequest) onDataReceived;
|
|
||||||
|
|
||||||
enum State {
|
|
||||||
/// The request has not yet been sent
|
|
||||||
unsent,
|
|
||||||
|
|
||||||
/// The send() method has been called, but no data is
|
|
||||||
/// sent on the socket yet because the connection is busy.
|
|
||||||
pendingAvailableConnection,
|
|
||||||
|
|
||||||
/// The headers are being sent now
|
|
||||||
sendingHeaders,
|
|
||||||
|
|
||||||
/// The body is being sent now
|
|
||||||
sendingBody,
|
|
||||||
|
|
||||||
/// The request has been sent but we haven't received any response yet
|
|
||||||
waitingForResponse,
|
|
||||||
|
|
||||||
/// We have received some data and are currently receiving headers
|
|
||||||
readingHeaders,
|
|
||||||
|
|
||||||
/// All headers are available but we're still waiting on the body
|
|
||||||
readingBody,
|
|
||||||
|
|
||||||
/// The request is complete.
|
|
||||||
complete,
|
|
||||||
|
|
||||||
/// The request is aborted, either by the abort() method, or as a result of the server disconnecting
|
|
||||||
aborted
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends now and waits for the request to finish, returning the response.
|
|
||||||
HttpResponse perform() {
|
|
||||||
send();
|
|
||||||
return waitForCompletion();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends the request asynchronously.
|
|
||||||
void send() {
|
|
||||||
sendPrivate(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendPrivate(bool advance) {
|
|
||||||
if(state != State.unsent && state != State.aborted)
|
|
||||||
return; // already sent
|
|
||||||
string headers;
|
|
||||||
|
|
||||||
headers ~= to!string(requestParameters.method) ~ " "~requestParameters.uri;
|
|
||||||
if(requestParameters.useHttp11)
|
|
||||||
headers ~= " HTTP/1.1\r\n";
|
|
||||||
else
|
|
||||||
headers ~= " HTTP/1.0\r\n";
|
|
||||||
headers ~= "Host: "~requestParameters.host~"\r\n";
|
|
||||||
if(requestParameters.userAgent.length)
|
|
||||||
headers ~= "User-Agent: "~requestParameters.userAgent~"\r\n";
|
|
||||||
if(requestParameters.contentType.length)
|
|
||||||
headers ~= "Content-Type: "~requestParameters.contentType~"\r\n";
|
|
||||||
if(requestParameters.authorization.length)
|
|
||||||
headers ~= "Authorization: "~requestParameters.authorization~"\r\n";
|
|
||||||
if(requestParameters.bodyData.length)
|
|
||||||
headers ~= "Content-Length: "~to!string(requestParameters.bodyData.length)~"\r\n";
|
|
||||||
if(requestParameters.acceptGzip)
|
|
||||||
headers ~= "Accept-Encoding: gzip\r\n";
|
|
||||||
|
|
||||||
foreach(header; requestParameters.headers)
|
|
||||||
headers ~= header ~ "\r\n";
|
|
||||||
|
|
||||||
headers ~= "\r\n";
|
|
||||||
|
|
||||||
sendBuffer = cast(ubyte[]) headers ~ requestParameters.bodyData;
|
|
||||||
|
|
||||||
// import std.stdio; writeln("******* ", sendBuffer);
|
|
||||||
|
|
||||||
responseData = HttpResponse.init;
|
|
||||||
responseData.requestParameters = requestParameters;
|
|
||||||
bodyBytesSent = 0;
|
|
||||||
bodyBytesReceived = 0;
|
|
||||||
state = State.pendingAvailableConnection;
|
|
||||||
|
|
||||||
bool alreadyPending = false;
|
|
||||||
foreach(req; pending)
|
|
||||||
if(req is this) {
|
|
||||||
alreadyPending = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(!alreadyPending) {
|
|
||||||
pending ~= this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(advance)
|
|
||||||
HttpRequest.advanceConnections();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Waits for the request to finish or timeout, whichever comes first.
|
|
||||||
HttpResponse waitForCompletion() {
|
|
||||||
while(state != State.aborted && state != State.complete) {
|
|
||||||
if(state == State.unsent)
|
|
||||||
send();
|
|
||||||
if(auto err = HttpRequest.advanceConnections())
|
|
||||||
throw new Exception("waitForCompletion got err " ~ to!string(err));
|
|
||||||
}
|
|
||||||
|
|
||||||
return responseData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Aborts this request.
|
|
||||||
void abort() {
|
|
||||||
this.state = State.aborted;
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequestParameters requestParameters; ///
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
46
jni.d
46
jni.d
|
@ -120,6 +120,24 @@
|
||||||
+/
|
+/
|
||||||
module arsd.jni;
|
module arsd.jni;
|
||||||
|
|
||||||
|
/*
|
||||||
|
New Java classes:
|
||||||
|
|
||||||
|
class Foo : extends!Bar {
|
||||||
|
|
||||||
|
mixin stuff;
|
||||||
|
}
|
||||||
|
mixin stuff;
|
||||||
|
|
||||||
|
The `extends` template creates a wrapper that calls the nonvirtual
|
||||||
|
methods, so `super()` just works.
|
||||||
|
|
||||||
|
receiving an object should perhaps always give a subclass that is javafied;
|
||||||
|
calls the virtuals, unless of course it is final.
|
||||||
|
|
||||||
|
dynamic downcasts of java objects will probably never work.
|
||||||
|
*/
|
||||||
|
|
||||||
/+
|
/+
|
||||||
For interfaces:
|
For interfaces:
|
||||||
|
|
||||||
|
@ -261,6 +279,7 @@ private string getJavaName(alias a)() {
|
||||||
version(WithClassLoadSupport) {
|
version(WithClassLoadSupport) {
|
||||||
import arsd.declarativeloader;
|
import arsd.declarativeloader;
|
||||||
|
|
||||||
|
/// translator.
|
||||||
void jarToD()(string jarPath, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc, bool delegate(string className) classFilter = null) {
|
void jarToD()(string jarPath, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc, bool delegate(string className) classFilter = null) {
|
||||||
import std.zip;
|
import std.zip;
|
||||||
import std.file;
|
import std.file;
|
||||||
|
@ -278,7 +297,7 @@ void jarToD()(string jarPath, string dPackagePrefix, string outputDirectory, Jav
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inout(char)[] fixupKeywordsInJavaPackageName(inout(char)[] s) {
|
private inout(char)[] fixupKeywordsInJavaPackageName(inout(char)[] s) {
|
||||||
import std.string;
|
import std.string;
|
||||||
s ~= "."; // lol i suck
|
s ~= "."; // lol i suck
|
||||||
s = s.replace(".function.", ".function_.");
|
s = s.replace(".function.", ".function_.");
|
||||||
|
@ -286,12 +305,13 @@ inout(char)[] fixupKeywordsInJavaPackageName(inout(char)[] s) {
|
||||||
return s[0 .. $-1]; // god i am such a bad programmer
|
return s[0 .. $-1]; // god i am such a bad programmer
|
||||||
}
|
}
|
||||||
|
|
||||||
inout(char)[] fixupJavaClassName(inout(char)[] s) {
|
private inout(char)[] fixupJavaClassName(inout(char)[] s) {
|
||||||
if(s == "Throwable" || s == "Object" || s == "Exception" || s == "Error" || s == "TypeInfo")
|
if(s == "Throwable" || s == "Object" || s == "Exception" || s == "Error" || s == "TypeInfo")
|
||||||
s = cast(typeof(s)) "Java" ~ s;
|
s = cast(typeof(s)) "Java" ~ s;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For the translator
|
||||||
struct JavaTranslationConfig {
|
struct JavaTranslationConfig {
|
||||||
/// List the Java methods, imported to D.
|
/// List the Java methods, imported to D.
|
||||||
bool doImports;
|
bool doImports;
|
||||||
|
@ -299,8 +319,11 @@ struct JavaTranslationConfig {
|
||||||
bool doExports;
|
bool doExports;
|
||||||
/// Put implementations inline. If false, this separates interface from impl for quicker builds with dmd -i.
|
/// Put implementations inline. If false, this separates interface from impl for quicker builds with dmd -i.
|
||||||
bool inlineImplementations;
|
bool inlineImplementations;
|
||||||
|
/// Treat native functions as imports, otherwise fills in as exports. Make sure doImports == true.
|
||||||
|
bool nativesAreImports = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// translator.
|
||||||
void rawClassBytesToD()(ubyte[] classBytes, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc) {
|
void rawClassBytesToD()(ubyte[] classBytes, string dPackagePrefix, string outputDirectory, JavaTranslationConfig jtc) {
|
||||||
import std.file;
|
import std.file;
|
||||||
import std.path;
|
import std.path;
|
||||||
|
@ -370,10 +393,15 @@ void rawClassBytesToD()(ubyte[] classBytes, string dPackagePrefix, string output
|
||||||
dc ~= (isInterface ? "interface " : "final class ") ~ lastClassName ~ " : IJavaObject {\n";
|
dc ~= (isInterface ? "interface " : "final class ") ~ lastClassName ~ " : IJavaObject {\n";
|
||||||
foreach(method; cf.methodsListing) {
|
foreach(method; cf.methodsListing) {
|
||||||
bool native = (method.flags & 0x0100) ? true : false;
|
bool native = (method.flags & 0x0100) ? true : false;
|
||||||
if(native && !jtc.doExports)
|
if(jtc.nativesAreImports) {
|
||||||
continue;
|
if(!jtc.doImports)
|
||||||
if(!native && !jtc.doImports)
|
continue;
|
||||||
continue;
|
} else {
|
||||||
|
if(native && !jtc.doExports)
|
||||||
|
continue;
|
||||||
|
if(!native && !jtc.doImports)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto port = native ? "@Export" : "@Import";
|
auto port = native ? "@Export" : "@Import";
|
||||||
if(method.flags & 1) { // public
|
if(method.flags & 1) { // public
|
||||||
|
|
||||||
|
@ -2084,3 +2112,9 @@ union jvalue
|
||||||
jdouble d;
|
jdouble d;
|
||||||
jobject l;
|
jobject l;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2019-2020, Adam D. Ruppe.
|
||||||
|
Boost license. or whatever.
|
||||||
|
Most work done in December 2019.
|
||||||
|
*/
|
||||||
|
|
|
@ -459,7 +459,7 @@
|
||||||
I live in the eastern United States, so I will most likely not be around at night in
|
I live in the eastern United States, so I will most likely not be around at night in
|
||||||
that US east timezone.
|
that US east timezone.
|
||||||
|
|
||||||
License: Copyright Adam D. Ruppe, 2011-2017. Released under the Boost Software License.
|
License: Copyright Adam D. Ruppe, 2011-2020. Released under the Boost Software License.
|
||||||
|
|
||||||
Building documentation: You may wish to use the `arsd.ddoc` file from my github with
|
Building documentation: You may wish to use the `arsd.ddoc` file from my github with
|
||||||
building the documentation for simpledisplay yourself. It will give it a bit more style.
|
building the documentation for simpledisplay yourself. It will give it a bit more style.
|
||||||
|
|
Loading…
Reference in New Issue