mirror of
https://github.com/golang/go.git
synced 2026-01-30 07:32:05 +03:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f75aafdf56 | ||
|
|
b261730e56 | ||
|
|
40712a9625 | ||
|
|
24f46bd34f | ||
|
|
c24b5d43a6 | ||
|
|
dc3612e0d1 | ||
|
|
6f12826a86 | ||
|
|
3129c67db7 | ||
|
|
bb8706890b | ||
|
|
287be1e42d | ||
|
|
308bdd0256 | ||
|
|
9b4e323e88 | ||
|
|
a5add8c726 | ||
|
|
e464c08bef | ||
|
|
c0f5de1ad2 | ||
|
|
1861ac159e | ||
|
|
759c2c48ea | ||
|
|
09fa0b8e95 | ||
|
|
0d81858868 | ||
|
|
681a667ced | ||
|
|
806a84d3f1 | ||
|
|
87d4bd1997 | ||
|
|
1e933ed7c0 | ||
|
|
78d024a588 | ||
|
|
7a62274065 | ||
|
|
219ca602ab | ||
|
|
26015b9563 | ||
|
|
9fde86b012 | ||
|
|
3a03e877cc | ||
|
|
10316757ce | ||
|
|
da070bed19 | ||
|
|
09fc3cc5df | ||
|
|
17a48e0a00 |
@@ -34,6 +34,7 @@ We encourage all Go users to subscribe to
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="/doc/go1.7">Go 1.7</a> <small>(August 2016)</small></li>
|
||||
<li><a href="/doc/go1.6">Go 1.6</a> <small>(February 2016)</small></li>
|
||||
<li><a href="/doc/go1.5">Go 1.5</a> <small>(August 2015)</small></li>
|
||||
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
|
||||
|
||||
@@ -30,6 +30,25 @@ to fix critical security problems in both Go 1.4 and Go 1.5 as they arise.
|
||||
See the <a href="/security">security policy</a> for more details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.7">go1.7 (released 2016/08/15)</h2>
|
||||
|
||||
<p>
|
||||
Go 1.7 is a major release of Go.
|
||||
Read the <a href="/doc/go1.7">Go 1.7 Release Notes</a> for more information.
|
||||
</p>
|
||||
|
||||
<h3 id="go1.7.minor">Minor revisions</h3>
|
||||
|
||||
<p>
|
||||
go1.7.1 (released 2016/09/07) includes fixes to the compiler, runtime,
|
||||
documentation, and the <code>compress/flate</code>, <code>hash/crc32</code>,
|
||||
<code>io</code> <code>net</code>, <code>net/http</code>,
|
||||
<code>path/filepath</code>, <code>reflect</code>, and <code>syscall</code>
|
||||
packages.
|
||||
See the <a href="https://github.com/golang/go/issues?q=milestone%3AGo1.7.1">Go
|
||||
1.7.1 milestone</a> on our issue tracker for details.
|
||||
</p>
|
||||
|
||||
<h2 id="go1.6">go1.6 (released 2016/02/17)</h2>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--{
|
||||
"Title": "Go 1.7 Release Notes DRAFT",
|
||||
"Title": "Go 1.7 Release Notes",
|
||||
"Path": "/doc/go1.7",
|
||||
"Template": true
|
||||
}-->
|
||||
@@ -25,15 +25,6 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
ul li { margin: 0.5em 0; }
|
||||
</style>
|
||||
|
||||
<p>
|
||||
<!-- TODO: REMOVE THIS COMMENT -->
|
||||
<!-- TODO: Also remove "DRAFT" in the "Title" at the top of this file. -->
|
||||
<i>NOTE: This is a DRAFT of the Go 1.7 release notes, prepared for the Go 1.7 beta.
|
||||
Go 1.7 has NOT yet been released.
|
||||
By our regular schedule, it is expected some time in August 2016.
|
||||
</i>
|
||||
</p>
|
||||
|
||||
<h2 id="introduction">Introduction to Go 1.7</h2>
|
||||
|
||||
<p>
|
||||
@@ -919,7 +910,7 @@ For example, the address on which a request received is
|
||||
<p>
|
||||
The server's <a href="/pkg/net/http/#Server.Serve"><code>Serve</code></a> method
|
||||
now only enables HTTP/2 support if the <code>Server.TLSConfig</code> field is <code>nil</code>
|
||||
or includes <code>"h2"</code> in its <code>TLSConfig.NextProto</code>.
|
||||
or includes <code>"h2"</code> in its <code>TLSConfig.NextProtos</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
238
doc/gopher/favicon.svg
Normal file
238
doc/gopher/favicon.svg
Normal file
@@ -0,0 +1,238 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32.000001"
|
||||
id="svg4416"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="favicon.svg"
|
||||
inkscape:export-filename="../../favicon.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90">
|
||||
<defs
|
||||
id="defs4418" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="17.966652"
|
||||
inkscape:cy="9.2991824"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
units="px"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:bbox-nodes="true"
|
||||
showguides="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1018"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-global="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid5148" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata4421">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="icon"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1020.3622)">
|
||||
<ellipse
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="ellipse4216"
|
||||
cx="-907.35657"
|
||||
cy="479.90009"
|
||||
rx="3.5793996"
|
||||
ry="3.8207953"
|
||||
transform="matrix(-0.49169095,-0.87076978,-0.87076978,0.49169095,0,0)"
|
||||
inkscape:transform-center-x="0.67794294"
|
||||
inkscape:transform-center-y="-2.3634048" />
|
||||
<ellipse
|
||||
inkscape:transform-center-y="-2.3633882"
|
||||
inkscape:transform-center-x="-0.67793718"
|
||||
transform="matrix(0.49169095,-0.87076978,0.87076978,0.49169095,0,0)"
|
||||
ry="3.8207953"
|
||||
rx="3.5793996"
|
||||
cy="507.8461"
|
||||
cx="-891.57654"
|
||||
id="ellipse4463"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#384e54;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 16.091693,1021.3642 c -1.105749,0.01 -2.210341,0.049 -3.31609,0.09 C 6.8422558,1021.6738 2,1026.3942 2,1032.3622 c 0,2.9786 0,13 0,20 l 28,0 c 0,-8 0,-16 0,-20 0,-5.9683 -4.667345,-10.4912 -10.59023,-10.908 -1.10575,-0.078 -2.212328,-0.099 -3.318077,-0.09 z"
|
||||
id="path4465"
|
||||
sodipodi:nodetypes="ccsccscc" />
|
||||
<path
|
||||
inkscape:transform-center-y="-1.3604657"
|
||||
inkscape:transform-center-x="-0.98424303"
|
||||
sodipodi:nodetypes="sssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4469"
|
||||
d="m 4.6078867,1025.0462 c 0.459564,0.2595 1.818262,1.2013 1.980983,1.648 0.183401,0.5035 0.159385,1.0657 -0.114614,1.551 -0.346627,0.6138 -1.005341,0.9487 -1.696421,0.9365 -0.339886,-0.01 -1.720283,-0.6372 -2.042561,-0.8192 -0.97754,-0.5519 -1.350795,-1.7418 -0.833686,-2.6576 0.517109,-0.9158 1.728749,-1.2107 2.706299,-0.6587 z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<rect
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32850246;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4473"
|
||||
width="3.0866659"
|
||||
height="3.5313663"
|
||||
x="14.406213"
|
||||
y="1035.6842"
|
||||
ry="0.62426329" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 16,1023.3622 c -9,0 -12,3.7153 -12,9 l 0,20 24,0 c -0.04889,-7.3562 0,-18 0,-20 0,-5.2848 -3,-9 -12,-9 z"
|
||||
id="path4471"
|
||||
sodipodi:nodetypes="zsccsz" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#76e1fe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 27.074073,1025.0462 c -0.45957,0.2595 -1.818257,1.2013 -1.980979,1.648 -0.183401,0.5035 -0.159384,1.0657 0.114614,1.551 0.346627,0.6138 1.005335,0.9487 1.696415,0.9365 0.33988,-0.01 1.72029,-0.6372 2.04256,-0.8192 0.97754,-0.5519 1.35079,-1.7418 0.83369,-2.6576 -0.51711,-0.9158 -1.72876,-1.2107 -2.7063,-0.6587 z"
|
||||
id="path4481"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssss"
|
||||
inkscape:transform-center-x="0.98424094"
|
||||
inkscape:transform-center-y="-1.3604657" />
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle4477"
|
||||
cx="21.175734"
|
||||
cy="1030.3542"
|
||||
r="4.6537542"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
r="4.8316345"
|
||||
cy="1030.3542"
|
||||
cx="10.339486"
|
||||
id="circle4483"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<rect
|
||||
inkscape:export-ydpi="90"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32941176;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="rect4246"
|
||||
width="3.6673687"
|
||||
height="4.1063409"
|
||||
x="14.115863"
|
||||
y="1035.9174"
|
||||
ry="0.72590536" />
|
||||
<rect
|
||||
ry="0.72590536"
|
||||
y="1035.2253"
|
||||
x="14.115863"
|
||||
height="4.1063409"
|
||||
width="3.6673687"
|
||||
id="rect4485"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#fffcfb;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:0.32941176;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
d="m 19.999735,1036.5289 c 0,0.838 -0.871228,1.2682 -2.144766,1.1659 -0.02366,0 -0.04795,-0.6004 -0.254147,-0.5832 -0.503669,0.042 -1.095902,-0.02 -1.685964,-0.02 -0.612939,0 -1.206342,0.1826 -1.68549,0.017 -0.110233,-0.038 -0.178298,0.5838 -0.261532,0.5816 -1.243685,-0.033 -2.078803,-0.3383 -2.078803,-1.1618 0,-1.2118 1.815635,-2.1941 4.055351,-2.1941 2.239704,0 4.055351,0.9823 4.055351,2.1941 z"
|
||||
id="path4487"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="sssssssss"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
sodipodi:nodetypes="sssssssss"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4489"
|
||||
d="m 19.977414,1035.7004 c 0,0.5685 -0.433659,0.8554 -1.138091,1.0001 -0.291933,0.06 -0.630371,0.096 -1.003719,0.1166 -0.56405,0.032 -1.207782,0.031 -1.89122,0.031 -0.672834,0 -1.307182,0 -1.864904,-0.029 -0.306268,-0.017 -0.589429,-0.043 -0.843164,-0.084 -0.813833,-0.1318 -1.324962,-0.417 -1.324962,-1.0344 0,-1.1601 1.805642,-2.1006 4.03303,-2.1006 2.227377,0 4.03303,0.9405 4.03303,2.1006 z"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c38c74;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<ellipse
|
||||
cy="1033.8501"
|
||||
cx="15.944382"
|
||||
id="ellipse4491"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#23201f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
rx="2.0801733"
|
||||
ry="1.343747"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#171311;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
id="circle4493"
|
||||
cx="12.414201"
|
||||
cy="1030.3542"
|
||||
r="1.9630634"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<circle
|
||||
r="1.9630634"
|
||||
cy="1030.3542"
|
||||
cx="23.110121"
|
||||
id="circle4495"
|
||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#171311;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:export-filename=".\rect4485.png"
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4497"
|
||||
d="m 5.0055377,1027.2727 c -1.170435,-1.0835 -2.026973,-0.7721 -2.044172,-0.7463"
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 4.3852457,1026.9152 c -1.158557,0.036 -1.346704,0.6303 -1.33881,0.6523"
|
||||
id="path4499"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 26.630533,1027.1724 c 1.17043,-1.0835 2.02697,-0.7721 2.04417,-0.7463"
|
||||
id="path4501"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4503"
|
||||
d="m 27.321773,1026.673 c 1.15856,0.036 1.3467,0.6302 1.3388,0.6522"
|
||||
style="display:inline;fill:none;fill-rule:evenodd;stroke:#384e54;stroke-width:0.39730874;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -203,7 +203,7 @@ To build without <code>cgo</code>, set the environment variable
|
||||
Change to the directory that will be its parent
|
||||
and make sure the <code>go</code> directory does not exist.
|
||||
Then clone the repository and check out the latest release tag
|
||||
(<code class="versionTag">go1.6</code>, for example):</p>
|
||||
(<code class="versionTag">go1.7.1</code>, for example):</p>
|
||||
|
||||
<pre>
|
||||
$ git clone https://go.googlesource.com/go
|
||||
@@ -391,7 +391,7 @@ New releases are announced on the
|
||||
<a href="//groups.google.com/group/golang-announce">golang-announce</a>
|
||||
mailing list.
|
||||
Each announcement mentions the latest release tag, for instance,
|
||||
<code class="versionTag">go1.6</code>.
|
||||
<code class="versionTag">go1.7.1</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<p>
|
||||
<a href="https://golang.org/dl/" target="_blank">Official binary
|
||||
distributions</a> are available for the FreeBSD (release 8-STABLE and above),
|
||||
Linux, Mac OS X (10.7 and above), and Windows operating systems and
|
||||
Linux, Mac OS X (10.8 and above), and Windows operating systems and
|
||||
the 32-bit (<code>386</code>) and 64-bit (<code>amd64</code>) x86 processor
|
||||
architectures.
|
||||
</p>
|
||||
@@ -49,7 +49,7 @@ If your OS or architecture is not on the list, you may be able to
|
||||
<tr><td colspan="3"><hr></td></tr>
|
||||
<tr><td>FreeBSD 8-STABLE or later</td> <td>amd64</td> <td>Debian GNU/kFreeBSD not supported</td></tr>
|
||||
<tr><td>Linux 2.6.23 or later with glibc</td> <td>amd64, 386, arm</td> <td>CentOS/RHEL 5.x not supported</td></tr>
|
||||
<tr><td>Mac OS X 10.7 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup></td></tr>
|
||||
<tr><td>Mac OS X 10.7 or later</td> <td>amd64</td> <td>use the clang or gcc<sup>†</sup> that comes with Xcode<sup>‡</sup> for <code>cgo</code> support</td></tr>
|
||||
<tr><td>Windows XP or later</td> <td>amd64, 386</td> <td>use MinGW gcc<sup>†</sup>. No need for cygwin or msys.</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
BIN
favicon.ico
BIN
favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 5.6 KiB |
@@ -14,8 +14,7 @@ func dse(f *Func) {
|
||||
defer f.retSparseSet(loadUse)
|
||||
storeUse := f.newSparseSet(f.NumValues())
|
||||
defer f.retSparseSet(storeUse)
|
||||
shadowed := f.newSparseSet(f.NumValues())
|
||||
defer f.retSparseSet(shadowed)
|
||||
shadowed := newSparseMap(f.NumValues()) // TODO: cache
|
||||
for _, b := range f.Blocks {
|
||||
// Find all the stores in this block. Categorize their uses:
|
||||
// loadUse contains stores which are used by a subsequent load.
|
||||
@@ -81,17 +80,18 @@ func dse(f *Func) {
|
||||
shadowed.clear()
|
||||
}
|
||||
if v.Op == OpStore || v.Op == OpZero {
|
||||
if shadowed.contains(v.Args[0].ID) {
|
||||
sz := v.AuxInt
|
||||
if shadowedSize := int64(shadowed.get(v.Args[0].ID)); shadowedSize != -1 && shadowedSize >= sz {
|
||||
// Modify store into a copy
|
||||
if v.Op == OpStore {
|
||||
// store addr value mem
|
||||
v.SetArgs1(v.Args[2])
|
||||
} else {
|
||||
// zero addr mem
|
||||
sz := v.Args[0].Type.ElemType().Size()
|
||||
if v.AuxInt != sz {
|
||||
typesz := v.Args[0].Type.ElemType().Size()
|
||||
if sz != typesz {
|
||||
f.Fatalf("mismatched zero/store sizes: %d and %d [%s]",
|
||||
v.AuxInt, sz, v.LongString())
|
||||
sz, typesz, v.LongString())
|
||||
}
|
||||
v.SetArgs1(v.Args[1])
|
||||
}
|
||||
@@ -99,7 +99,10 @@ func dse(f *Func) {
|
||||
v.AuxInt = 0
|
||||
v.Op = OpCopy
|
||||
} else {
|
||||
shadowed.add(v.Args[0].ID)
|
||||
if sz > 0x7fffffff { // work around sparseMap's int32 value type
|
||||
sz = 0x7fffffff
|
||||
}
|
||||
shadowed.set(v.Args[0].ID, int32(sz))
|
||||
}
|
||||
}
|
||||
// walk to previous store
|
||||
|
||||
@@ -8,7 +8,7 @@ import "testing"
|
||||
|
||||
func TestDeadStore(t *testing.T) {
|
||||
c := testConfig(t)
|
||||
elemType := &TypeImpl{Size_: 8, Name: "testtype"}
|
||||
elemType := &TypeImpl{Size_: 1, Name: "testtype"}
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr", Elem_: elemType} // dummy for testing
|
||||
fun := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
@@ -18,7 +18,7 @@ func TestDeadStore(t *testing.T) {
|
||||
Valu("addr1", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("addr2", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("addr3", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("zero1", OpZero, TypeMem, 8, nil, "addr3", "start"),
|
||||
Valu("zero1", OpZero, TypeMem, 1, nil, "addr3", "start"),
|
||||
Valu("store1", OpStore, TypeMem, 1, nil, "addr1", "v", "zero1"),
|
||||
Valu("store2", OpStore, TypeMem, 1, nil, "addr2", "v", "store1"),
|
||||
Valu("store3", OpStore, TypeMem, 1, nil, "addr1", "v", "store2"),
|
||||
@@ -95,3 +95,32 @@ func TestDeadStoreTypes(t *testing.T) {
|
||||
t.Errorf("store %s incorrectly removed", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeadStoreUnsafe(t *testing.T) {
|
||||
// Make sure a narrow store can't shadow a wider one. The test above
|
||||
// covers the case of two different types, but unsafe pointer casting
|
||||
// can get to a point where the size is changed but type unchanged.
|
||||
c := testConfig(t)
|
||||
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
|
||||
fun := Fun(c, "entry",
|
||||
Bloc("entry",
|
||||
Valu("start", OpInitMem, TypeMem, 0, nil),
|
||||
Valu("sb", OpSB, TypeInvalid, 0, nil),
|
||||
Valu("v", OpConstBool, TypeBool, 1, nil),
|
||||
Valu("addr1", OpAddr, ptrType, 0, nil, "sb"),
|
||||
Valu("store1", OpStore, TypeMem, 8, nil, "addr1", "v", "start"), // store 8 bytes
|
||||
Valu("store2", OpStore, TypeMem, 1, nil, "addr1", "v", "store1"), // store 1 byte
|
||||
Goto("exit")),
|
||||
Bloc("exit",
|
||||
Exit("store2")))
|
||||
|
||||
CheckFunc(fun.f)
|
||||
cse(fun.f)
|
||||
dse(fun.f)
|
||||
CheckFunc(fun.f)
|
||||
|
||||
v := fun.values["store1"]
|
||||
if v.Op == OpCopy {
|
||||
t.Errorf("store %s incorrectly removed", v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,7 +724,7 @@ func (w *Writer) Close() error {
|
||||
// the result of NewWriter or NewWriterDict called with dst
|
||||
// and w's level and dictionary.
|
||||
func (w *Writer) Reset(dst io.Writer) {
|
||||
if dw, ok := w.d.w.w.(*dictWriter); ok {
|
||||
if dw, ok := w.d.w.writer.(*dictWriter); ok {
|
||||
// w was created with NewWriterDict
|
||||
dw.w = dst
|
||||
w.d.reset(dw)
|
||||
|
||||
@@ -6,6 +6,7 @@ package flate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
@@ -631,3 +632,52 @@ func TestBestSpeed(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errIO = errors.New("IO error")
|
||||
|
||||
// failWriter fails with errIO exactly at the nth call to Write.
|
||||
type failWriter struct{ n int }
|
||||
|
||||
func (w *failWriter) Write(b []byte) (int, error) {
|
||||
w.n--
|
||||
if w.n == -1 {
|
||||
return 0, errIO
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func TestWriterPersistentError(t *testing.T) {
|
||||
d, err := ioutil.ReadFile("../testdata/Mark.Twain-Tom.Sawyer.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
d = d[:10000] // Keep this test short
|
||||
|
||||
zw, err := NewWriter(nil, DefaultCompression)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWriter: %v", err)
|
||||
}
|
||||
|
||||
// Sweep over the threshold at which an error is returned.
|
||||
// The variable i makes it such that the ith call to failWriter.Write will
|
||||
// return errIO. Since failWriter errors are not persistent, we must ensure
|
||||
// that flate.Writer errors are persistent.
|
||||
for i := 0; i < 1000; i++ {
|
||||
fw := &failWriter{i}
|
||||
zw.Reset(fw)
|
||||
|
||||
_, werr := zw.Write(d)
|
||||
cerr := zw.Close()
|
||||
if werr != errIO && werr != nil {
|
||||
t.Errorf("test %d, mismatching Write error: got %v, want %v", i, werr, errIO)
|
||||
}
|
||||
if cerr != errIO && fw.n < 0 {
|
||||
t.Errorf("test %d, mismatching Close error: got %v, want %v", i, cerr, errIO)
|
||||
}
|
||||
if fw.n >= 0 {
|
||||
// At this point, the failure threshold was sufficiently high enough
|
||||
// that we wrote the whole stream without any errors.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,11 @@ var offsetBase = []uint32{
|
||||
var codegenOrder = []uint32{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
|
||||
|
||||
type huffmanBitWriter struct {
|
||||
w io.Writer
|
||||
// writer is the underlying writer.
|
||||
// Do not use it directly; use the write method, which ensures
|
||||
// that Write errors are sticky.
|
||||
writer io.Writer
|
||||
|
||||
// Data waiting to be written is bytes[0:nbytes]
|
||||
// and then the low nbits of bits.
|
||||
bits uint64
|
||||
@@ -96,7 +100,7 @@ type huffmanBitWriter struct {
|
||||
|
||||
func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
|
||||
return &huffmanBitWriter{
|
||||
w: w,
|
||||
writer: w,
|
||||
literalFreq: make([]int32, maxNumLit),
|
||||
offsetFreq: make([]int32, offsetCodeCount),
|
||||
codegen: make([]uint8, maxNumLit+offsetCodeCount+1),
|
||||
@@ -107,7 +111,7 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter {
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) reset(writer io.Writer) {
|
||||
w.w = writer
|
||||
w.writer = writer
|
||||
w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil
|
||||
w.bytes = [bufferSize]byte{}
|
||||
}
|
||||
@@ -129,11 +133,21 @@ func (w *huffmanBitWriter) flush() {
|
||||
n++
|
||||
}
|
||||
w.bits = 0
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
w.nbytes = 0
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) write(b []byte) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
_, w.err = w.writer.Write(b)
|
||||
}
|
||||
|
||||
func (w *huffmanBitWriter) writeBits(b int32, nb uint) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.bits |= uint64(b) << w.nbits
|
||||
w.nbits += nb
|
||||
if w.nbits >= 48 {
|
||||
@@ -150,7 +164,7 @@ func (w *huffmanBitWriter) writeBits(b int32, nb uint) {
|
||||
bytes[5] = byte(bits >> 40)
|
||||
n += 6
|
||||
if n >= bufferFlushSize {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
n = 0
|
||||
}
|
||||
w.nbytes = n
|
||||
@@ -173,13 +187,10 @@ func (w *huffmanBitWriter) writeBytes(bytes []byte) {
|
||||
n++
|
||||
}
|
||||
if n != 0 {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
w.write(w.bytes[:n])
|
||||
}
|
||||
w.nbytes = 0
|
||||
_, w.err = w.w.Write(bytes)
|
||||
w.write(bytes)
|
||||
}
|
||||
|
||||
// RFC 1951 3.2.7 specifies a special run-length encoding for specifying
|
||||
@@ -341,7 +352,7 @@ func (w *huffmanBitWriter) writeCode(c hcode) {
|
||||
bytes[5] = byte(bits >> 40)
|
||||
n += 6
|
||||
if n >= bufferFlushSize {
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
n = 0
|
||||
}
|
||||
w.nbytes = n
|
||||
@@ -572,6 +583,9 @@ func (w *huffmanBitWriter) indexTokens(tokens []token) (numLiterals, numOffsets
|
||||
// writeTokens writes a slice of tokens to the output.
|
||||
// codes for literal and offset encoding must be supplied.
|
||||
func (w *huffmanBitWriter) writeTokens(tokens []token, leCodes, oeCodes []hcode) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
for _, t := range tokens {
|
||||
if t < matchType {
|
||||
w.writeCode(leCodes[t.literal()])
|
||||
@@ -676,9 +690,9 @@ func (w *huffmanBitWriter) writeBlockHuff(eof bool, input []byte) {
|
||||
if n < bufferFlushSize {
|
||||
continue
|
||||
}
|
||||
_, w.err = w.w.Write(w.bytes[:n])
|
||||
w.write(w.bytes[:n])
|
||||
if w.err != nil {
|
||||
return
|
||||
return // Return early in the event of write failures
|
||||
}
|
||||
n = 0
|
||||
}
|
||||
|
||||
@@ -4,14 +4,9 @@
|
||||
|
||||
package crc32
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
vxMinLen = 64
|
||||
vxAlignment = 16
|
||||
vxAlignMask = vxAlignment - 1
|
||||
vxAlignMask = 15 // align to 16 bytes
|
||||
)
|
||||
|
||||
// hasVectorFacility reports whether the machine has the z/Architecture
|
||||
@@ -49,20 +44,13 @@ func genericIEEE(crc uint32, p []byte) uint32 {
|
||||
return update(crc, IEEETable, p)
|
||||
}
|
||||
|
||||
// updateCastagnoli calculates the checksum of p using genericCastagnoli to
|
||||
// align the data appropriately for vectorCastagnoli. It avoids using
|
||||
// vectorCastagnoli entirely if the length of p is less than or equal to
|
||||
// vxMinLen.
|
||||
// updateCastagnoli calculates the checksum of p using
|
||||
// vectorizedCastagnoli if possible and falling back onto
|
||||
// genericCastagnoli as needed.
|
||||
func updateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
// Use vectorized function if vector facility is available and
|
||||
// data length is above threshold.
|
||||
if hasVX && len(p) > vxMinLen {
|
||||
pAddr := uintptr(unsafe.Pointer(&p[0]))
|
||||
if pAddr&vxAlignMask != 0 {
|
||||
prealign := vxAlignment - int(pAddr&vxAlignMask)
|
||||
crc = genericCastagnoli(crc, p[:prealign])
|
||||
p = p[prealign:]
|
||||
}
|
||||
if hasVX && len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedCastagnoli(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
@@ -75,19 +63,12 @@ func updateCastagnoli(crc uint32, p []byte) uint32 {
|
||||
return genericCastagnoli(crc, p)
|
||||
}
|
||||
|
||||
// updateIEEE calculates the checksum of p using genericIEEE to align the data
|
||||
// appropriately for vectorIEEE. It avoids using vectorIEEE entirely if the length
|
||||
// of p is less than or equal to vxMinLen.
|
||||
// updateIEEE calculates the checksum of p using vectorizedIEEE if
|
||||
// possible and falling back onto genericIEEE as needed.
|
||||
func updateIEEE(crc uint32, p []byte) uint32 {
|
||||
// Use vectorized function if vector facility is available and
|
||||
// data length is above threshold.
|
||||
if hasVX && len(p) > vxMinLen {
|
||||
pAddr := uintptr(unsafe.Pointer(&p[0]))
|
||||
if pAddr&vxAlignMask != 0 {
|
||||
prealign := vxAlignment - int(pAddr&vxAlignMask)
|
||||
crc = genericIEEE(crc, p[:prealign])
|
||||
p = p[prealign:]
|
||||
}
|
||||
if hasVX && len(p) >= vxMinLen {
|
||||
aligned := len(p) & ^vxAlignMask
|
||||
crc = vectorizedIEEE(crc, p[:aligned])
|
||||
p = p[aligned:]
|
||||
|
||||
@@ -128,6 +128,10 @@ TEXT vectorizedBody<>(SB),NOSPLIT,$0
|
||||
VZERO V0
|
||||
VLVGF $3, R2, V0
|
||||
|
||||
// Crash if the input size is less than 64-bytes.
|
||||
CMP R4, $64
|
||||
BLT crash
|
||||
|
||||
// Load a 64-byte data chunk and XOR with CRC
|
||||
VLM 0(R3), V1, V4 // 64-bytes into V1..V4
|
||||
|
||||
@@ -243,3 +247,6 @@ done:
|
||||
XOR $0xffffffff, R2 // NOTW R2
|
||||
MOVWZ R2, ret + 32(FP)
|
||||
RET
|
||||
|
||||
crash:
|
||||
MOVD $0, (R0) // input size is less than 64-bytes
|
||||
|
||||
@@ -18,15 +18,16 @@ func (mr *multiReader) Read(p []byte) (n int, err error) {
|
||||
}
|
||||
}
|
||||
n, err = mr.readers[0].Read(p)
|
||||
if err == EOF {
|
||||
mr.readers = mr.readers[1:]
|
||||
}
|
||||
if n > 0 || err != EOF {
|
||||
if err == EOF {
|
||||
// Don't return EOF yet. There may be more bytes
|
||||
// in the remaining readers.
|
||||
if err == EOF && len(mr.readers) > 0 {
|
||||
// Don't return EOF yet. More readers remain.
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
mr.readers = mr.readers[1:]
|
||||
}
|
||||
return 0, EOF
|
||||
}
|
||||
|
||||
@@ -196,3 +196,41 @@ func TestMultiReaderFlatten(t *testing.T) {
|
||||
myDepth+2, readDepth)
|
||||
}
|
||||
}
|
||||
|
||||
// byteAndEOFReader is a Reader which reads one byte (the underlying
|
||||
// byte) and io.EOF at once in its Read call.
|
||||
type byteAndEOFReader byte
|
||||
|
||||
func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
|
||||
if len(p) == 0 {
|
||||
// Read(0 bytes) is useless. We expect no such useless
|
||||
// calls in this test.
|
||||
panic("unexpected call")
|
||||
}
|
||||
p[0] = byte(b)
|
||||
return 1, EOF
|
||||
}
|
||||
|
||||
// In Go 1.7, this yielded bytes forever.
|
||||
func TestMultiReaderSingleByteWithEOF(t *testing.T) {
|
||||
got, err := ioutil.ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const want = "ab"
|
||||
if string(got) != want {
|
||||
t.Errorf("got %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a reader returning (n, EOF) at the end of an MultiReader
|
||||
// chain continues to return EOF on its final read, rather than
|
||||
// yielding a (0, EOF).
|
||||
func TestMultiReaderFinalEOF(t *testing.T) {
|
||||
r := MultiReader(bytes.NewReader(nil), byteAndEOFReader('a'))
|
||||
buf := make([]byte, 2)
|
||||
n, err := r.Read(buf)
|
||||
if n != 1 || err != EOF {
|
||||
t.Errorf("got %v, %v; want 1, EOF", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func (d *Dialer) dialDNS(ctx context.Context, network, server string) (dnsConn,
|
||||
}
|
||||
|
||||
// exchange sends a query on the connection and hopes for a response.
|
||||
func exchange(ctx context.Context, server, name string, qtype uint16) (*dnsMsg, error) {
|
||||
func exchange(ctx context.Context, server, name string, qtype uint16, timeout time.Duration) (*dnsMsg, error) {
|
||||
d := testHookDNSDialer()
|
||||
out := dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
@@ -152,6 +152,12 @@ func exchange(ctx context.Context, server, name string, qtype uint16) (*dnsMsg,
|
||||
},
|
||||
}
|
||||
for _, network := range []string{"udp", "tcp"} {
|
||||
// TODO(mdempsky): Refactor so defers from UDP-based
|
||||
// exchanges happen before TCP-based exchange.
|
||||
|
||||
ctx, cancel := context.WithDeadline(ctx, time.Now().Add(timeout))
|
||||
defer cancel()
|
||||
|
||||
c, err := d.dialDNS(ctx, network, server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -180,17 +186,10 @@ func tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype uint16)
|
||||
return "", nil, &DNSError{Err: "no DNS servers", Name: name}
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(cfg.timeout)
|
||||
if old, ok := ctx.Deadline(); !ok || deadline.Before(old) {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(ctx, deadline)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for i := 0; i < cfg.attempts; i++ {
|
||||
for _, server := range cfg.servers {
|
||||
msg, err := exchange(ctx, server, name, qtype)
|
||||
msg, err := exchange(ctx, server, name, qtype, cfg.timeout)
|
||||
if err != nil {
|
||||
lastErr = &DNSError{
|
||||
Err: err.Error(),
|
||||
@@ -338,8 +337,9 @@ func lookup(ctx context.Context, name string, qtype uint16) (cname string, rrs [
|
||||
}
|
||||
|
||||
// avoidDNS reports whether this is a hostname for which we should not
|
||||
// use DNS. Currently this includes only .onion and .local names,
|
||||
// per RFC 7686 and RFC 6762, respectively. See golang.org/issue/13705.
|
||||
// use DNS. Currently this includes only .onion, per RFC 7686. See
|
||||
// golang.org/issue/13705. Does not cover .local names (RFC 6762),
|
||||
// see golang.org/issue/16739.
|
||||
func avoidDNS(name string) bool {
|
||||
if name == "" {
|
||||
return true
|
||||
@@ -347,7 +347,7 @@ func avoidDNS(name string) bool {
|
||||
if name[len(name)-1] == '.' {
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
return stringsHasSuffixFold(name, ".onion") || stringsHasSuffixFold(name, ".local")
|
||||
return stringsHasSuffixFold(name, ".onion")
|
||||
}
|
||||
|
||||
// nameList returns a list of names for sequential DNS queries.
|
||||
|
||||
@@ -40,9 +40,9 @@ func TestDNSTransportFallback(t *testing.T) {
|
||||
testenv.MustHaveExternalNetwork(t)
|
||||
|
||||
for _, tt := range dnsTransportFallbackTests {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(tt.timeout)*time.Second)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
msg, err := exchange(ctx, tt.server, tt.name, tt.qtype)
|
||||
msg, err := exchange(ctx, tt.server, tt.name, tt.qtype, time.Second)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
@@ -82,9 +82,9 @@ func TestSpecialDomainName(t *testing.T) {
|
||||
|
||||
server := "8.8.8.8:53"
|
||||
for _, tt := range specialDomainNameTests {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
msg, err := exchange(ctx, server, tt.name, tt.qtype)
|
||||
msg, err := exchange(ctx, server, tt.name, tt.qtype, 3*time.Second)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
@@ -112,10 +112,11 @@ func TestAvoidDNSName(t *testing.T) {
|
||||
{"foo.ONION", true},
|
||||
{"foo.ONION.", true},
|
||||
|
||||
{"foo.local.", true},
|
||||
{"foo.local", true},
|
||||
{"foo.LOCAL", true},
|
||||
{"foo.LOCAL.", true},
|
||||
// But do resolve *.local address; Issue 16739
|
||||
{"foo.local.", false},
|
||||
{"foo.local", false},
|
||||
{"foo.LOCAL", false},
|
||||
{"foo.LOCAL.", false},
|
||||
|
||||
{"", true}, // will be rejected earlier too
|
||||
|
||||
@@ -500,7 +501,7 @@ func TestErrorForOriginalNameWhenSearching(t *testing.T) {
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
|
||||
d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
id: q.id,
|
||||
@@ -539,14 +540,15 @@ func TestIgnoreLameReferrals(t *testing.T) {
|
||||
}
|
||||
defer conf.teardown()
|
||||
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", "nameserver 192.0.2.2"}); err != nil {
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will give a lame referral
|
||||
"nameserver 192.0.2.2"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
d.rh = func(s string, q *dnsMsg) (*dnsMsg, error) {
|
||||
d.rh = func(s string, q *dnsMsg, _ time.Time) (*dnsMsg, error) {
|
||||
t.Log(s, q)
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
@@ -633,28 +635,30 @@ func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
|
||||
|
||||
type fakeDNSDialer struct {
|
||||
// reply handler
|
||||
rh func(s string, q *dnsMsg) (*dnsMsg, error)
|
||||
rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
|
||||
}
|
||||
|
||||
func (f *fakeDNSDialer) dialDNS(_ context.Context, n, s string) (dnsConn, error) {
|
||||
return &fakeDNSConn{f.rh, s}, nil
|
||||
return &fakeDNSConn{f.rh, s, time.Time{}}, nil
|
||||
}
|
||||
|
||||
type fakeDNSConn struct {
|
||||
rh func(s string, q *dnsMsg) (*dnsMsg, error)
|
||||
rh func(s string, q *dnsMsg, t time.Time) (*dnsMsg, error)
|
||||
s string
|
||||
t time.Time
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) SetDeadline(time.Time) error {
|
||||
func (f *fakeDNSConn) SetDeadline(t time.Time) error {
|
||||
f.t = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *fakeDNSConn) dnsRoundTrip(q *dnsMsg) (*dnsMsg, error) {
|
||||
return f.rh(f.s, q)
|
||||
return f.rh(f.s, q, f.t)
|
||||
}
|
||||
|
||||
// UDP round-tripper algorithm should ignore invalid DNS responses (issue 13281).
|
||||
@@ -724,3 +728,72 @@ func TestIgnoreDNSForgeries(t *testing.T) {
|
||||
t.Errorf("got address %v, want %v", got, TestAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 16865. If a name server times out, continue to the next.
|
||||
func TestRetryTimeout(t *testing.T) {
|
||||
origTestHookDNSDialer := testHookDNSDialer
|
||||
defer func() { testHookDNSDialer = origTestHookDNSDialer }()
|
||||
|
||||
conf, err := newResolvConfTest()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conf.teardown()
|
||||
|
||||
if err := conf.writeAndUpdate([]string{"nameserver 192.0.2.1", // the one that will timeout
|
||||
"nameserver 192.0.2.2"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := &fakeDNSDialer{}
|
||||
testHookDNSDialer = func() dnsDialer { return d }
|
||||
|
||||
var deadline0 time.Time
|
||||
|
||||
d.rh = func(s string, q *dnsMsg, deadline time.Time) (*dnsMsg, error) {
|
||||
t.Log(s, q, deadline)
|
||||
|
||||
if deadline.IsZero() {
|
||||
t.Error("zero deadline")
|
||||
}
|
||||
|
||||
if s == "192.0.2.1:53" {
|
||||
deadline0 = deadline
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return nil, errTimeout
|
||||
}
|
||||
|
||||
if deadline == deadline0 {
|
||||
t.Error("deadline didn't change")
|
||||
}
|
||||
|
||||
r := &dnsMsg{
|
||||
dnsMsgHdr: dnsMsgHdr{
|
||||
id: q.id,
|
||||
response: true,
|
||||
recursion_available: true,
|
||||
},
|
||||
question: q.question,
|
||||
answer: []dnsRR{
|
||||
&dnsRR_CNAME{
|
||||
Hdr: dnsRR_Header{
|
||||
Name: q.question[0].Name,
|
||||
Rrtype: dnsTypeCNAME,
|
||||
Class: dnsClassINET,
|
||||
},
|
||||
Cname: "golang.org",
|
||||
},
|
||||
},
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
_, err = goLookupCNAME(context.Background(), "www.golang.org")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if deadline0.IsZero() {
|
||||
t.Error("deadline0 still zero", deadline0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"net"
|
||||
"net/http/httptrace"
|
||||
"net/textproto"
|
||||
@@ -403,9 +404,17 @@ func (e http2ConnectionError) Error() string {
|
||||
type http2StreamError struct {
|
||||
StreamID uint32
|
||||
Code http2ErrCode
|
||||
Cause error // optional additional detail
|
||||
}
|
||||
|
||||
func http2streamError(id uint32, code http2ErrCode) http2StreamError {
|
||||
return http2StreamError{StreamID: id, Code: code}
|
||||
}
|
||||
|
||||
func (e http2StreamError) Error() string {
|
||||
if e.Cause != nil {
|
||||
return fmt.Sprintf("stream error: stream ID %d; %v; %v", e.StreamID, e.Code, e.Cause)
|
||||
}
|
||||
return fmt.Sprintf("stream error: stream ID %d; %v", e.StreamID, e.Code)
|
||||
}
|
||||
|
||||
@@ -1366,7 +1375,7 @@ func http2parseWindowUpdateFrame(fh http2FrameHeader, p []byte) (http2Frame, err
|
||||
if fh.StreamID == 0 {
|
||||
return nil, http2ConnectionError(http2ErrCodeProtocol)
|
||||
}
|
||||
return nil, http2StreamError{fh.StreamID, http2ErrCodeProtocol}
|
||||
return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol)
|
||||
}
|
||||
return &http2WindowUpdateFrame{
|
||||
http2FrameHeader: fh,
|
||||
@@ -1444,7 +1453,7 @@ func http2parseHeadersFrame(fh http2FrameHeader, p []byte) (_ http2Frame, err er
|
||||
}
|
||||
}
|
||||
if len(p)-int(padLength) <= 0 {
|
||||
return nil, http2StreamError{fh.StreamID, http2ErrCodeProtocol}
|
||||
return nil, http2streamError(fh.StreamID, http2ErrCodeProtocol)
|
||||
}
|
||||
hf.headerFragBuf = p[:len(p)-int(padLength)]
|
||||
return hf, nil
|
||||
@@ -1911,6 +1920,9 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr
|
||||
hdec.SetEmitEnabled(true)
|
||||
hdec.SetMaxStringLength(fr.maxHeaderStringLen())
|
||||
hdec.SetEmitFunc(func(hf hpack.HeaderField) {
|
||||
if http2VerboseLogs && http2logFrameReads {
|
||||
log.Printf("http2: decoded hpack field %+v", hf)
|
||||
}
|
||||
if !httplex.ValidHeaderFieldValue(hf.Value) {
|
||||
invalid = http2headerFieldValueError(hf.Value)
|
||||
}
|
||||
@@ -1969,11 +1981,17 @@ func (fr *http2Framer) readMetaFrame(hf *http2HeadersFrame) (*http2MetaHeadersFr
|
||||
}
|
||||
if invalid != nil {
|
||||
fr.errDetail = invalid
|
||||
return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol}
|
||||
if http2VerboseLogs {
|
||||
log.Printf("http2: invalid header: %v", invalid)
|
||||
}
|
||||
return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol, invalid}
|
||||
}
|
||||
if err := mh.checkPseudos(); err != nil {
|
||||
fr.errDetail = err
|
||||
return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol}
|
||||
if http2VerboseLogs {
|
||||
log.Printf("http2: invalid pseudo headers: %v", err)
|
||||
}
|
||||
return nil, http2StreamError{mh.StreamID, http2ErrCodeProtocol, err}
|
||||
}
|
||||
return mh, nil
|
||||
}
|
||||
@@ -3604,7 +3622,7 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
|
||||
case http2stateOpen:
|
||||
|
||||
st.state = http2stateHalfClosedLocal
|
||||
errCancel := http2StreamError{st.id, http2ErrCodeCancel}
|
||||
errCancel := http2streamError(st.id, http2ErrCodeCancel)
|
||||
sc.resetStream(errCancel)
|
||||
case http2stateHalfClosedRemote:
|
||||
sc.closeStream(st, http2errHandlerComplete)
|
||||
@@ -3797,7 +3815,7 @@ func (sc *http2serverConn) processWindowUpdate(f *http2WindowUpdateFrame) error
|
||||
return nil
|
||||
}
|
||||
if !st.flow.add(int32(f.Increment)) {
|
||||
return http2StreamError{f.StreamID, http2ErrCodeFlowControl}
|
||||
return http2streamError(f.StreamID, http2ErrCodeFlowControl)
|
||||
}
|
||||
default:
|
||||
if !sc.flow.add(int32(f.Increment)) {
|
||||
@@ -3819,7 +3837,7 @@ func (sc *http2serverConn) processResetStream(f *http2RSTStreamFrame) error {
|
||||
if st != nil {
|
||||
st.gotReset = true
|
||||
st.cancelCtx()
|
||||
sc.closeStream(st, http2StreamError{f.StreamID, f.ErrCode})
|
||||
sc.closeStream(st, http2streamError(f.StreamID, f.ErrCode))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -3922,13 +3940,13 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
|
||||
if !ok || st.state != http2stateOpen || st.gotTrailerHeader {
|
||||
|
||||
if sc.inflow.available() < int32(f.Length) {
|
||||
return http2StreamError{id, http2ErrCodeFlowControl}
|
||||
return http2streamError(id, http2ErrCodeFlowControl)
|
||||
}
|
||||
|
||||
sc.inflow.take(int32(f.Length))
|
||||
sc.sendWindowUpdate(nil, int(f.Length))
|
||||
|
||||
return http2StreamError{id, http2ErrCodeStreamClosed}
|
||||
return http2streamError(id, http2ErrCodeStreamClosed)
|
||||
}
|
||||
if st.body == nil {
|
||||
panic("internal error: should have a body in this state")
|
||||
@@ -3936,19 +3954,19 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
|
||||
|
||||
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
|
||||
st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
|
||||
return http2StreamError{id, http2ErrCodeStreamClosed}
|
||||
return http2streamError(id, http2ErrCodeStreamClosed)
|
||||
}
|
||||
if f.Length > 0 {
|
||||
|
||||
if st.inflow.available() < int32(f.Length) {
|
||||
return http2StreamError{id, http2ErrCodeFlowControl}
|
||||
return http2streamError(id, http2ErrCodeFlowControl)
|
||||
}
|
||||
st.inflow.take(int32(f.Length))
|
||||
|
||||
if len(data) > 0 {
|
||||
wrote, err := st.body.Write(data)
|
||||
if err != nil {
|
||||
return http2StreamError{id, http2ErrCodeStreamClosed}
|
||||
return http2streamError(id, http2ErrCodeStreamClosed)
|
||||
}
|
||||
if wrote != len(data) {
|
||||
panic("internal error: bad Writer")
|
||||
@@ -4046,10 +4064,10 @@ func (sc *http2serverConn) processHeaders(f *http2MetaHeadersFrame) error {
|
||||
|
||||
if sc.unackedSettings == 0 {
|
||||
|
||||
return http2StreamError{st.id, http2ErrCodeProtocol}
|
||||
return http2streamError(st.id, http2ErrCodeProtocol)
|
||||
}
|
||||
|
||||
return http2StreamError{st.id, http2ErrCodeRefusedStream}
|
||||
return http2streamError(st.id, http2ErrCodeRefusedStream)
|
||||
}
|
||||
|
||||
rw, req, err := sc.newWriterAndRequest(st, f)
|
||||
@@ -4083,18 +4101,18 @@ func (st *http2stream) processTrailerHeaders(f *http2MetaHeadersFrame) error {
|
||||
}
|
||||
st.gotTrailerHeader = true
|
||||
if !f.StreamEnded() {
|
||||
return http2StreamError{st.id, http2ErrCodeProtocol}
|
||||
return http2streamError(st.id, http2ErrCodeProtocol)
|
||||
}
|
||||
|
||||
if len(f.PseudoFields()) > 0 {
|
||||
return http2StreamError{st.id, http2ErrCodeProtocol}
|
||||
return http2streamError(st.id, http2ErrCodeProtocol)
|
||||
}
|
||||
if st.trailer != nil {
|
||||
for _, hf := range f.RegularFields() {
|
||||
key := sc.canonicalHeader(hf.Name)
|
||||
if !http2ValidTrailerHeader(key) {
|
||||
|
||||
return http2StreamError{st.id, http2ErrCodeProtocol}
|
||||
return http2streamError(st.id, http2ErrCodeProtocol)
|
||||
}
|
||||
st.trailer[key] = append(st.trailer[key], hf.Value)
|
||||
}
|
||||
@@ -4148,18 +4166,18 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead
|
||||
isConnect := method == "CONNECT"
|
||||
if isConnect {
|
||||
if path != "" || scheme != "" || authority == "" {
|
||||
return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol}
|
||||
return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
|
||||
}
|
||||
} else if method == "" || path == "" ||
|
||||
(scheme != "https" && scheme != "http") {
|
||||
|
||||
return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol}
|
||||
return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
|
||||
}
|
||||
|
||||
bodyOpen := !f.StreamEnded()
|
||||
if method == "HEAD" && bodyOpen {
|
||||
|
||||
return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol}
|
||||
return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
|
||||
}
|
||||
var tlsState *tls.ConnectionState // nil if not scheme https
|
||||
|
||||
@@ -4216,7 +4234,7 @@ func (sc *http2serverConn) newWriterAndRequest(st *http2stream, f *http2MetaHead
|
||||
var err error
|
||||
url_, err = url.ParseRequestURI(path)
|
||||
if err != nil {
|
||||
return nil, nil, http2StreamError{f.StreamID, http2ErrCodeProtocol}
|
||||
return nil, nil, http2streamError(f.StreamID, http2ErrCodeProtocol)
|
||||
}
|
||||
requestURI = path
|
||||
}
|
||||
@@ -4993,14 +5011,14 @@ type http2ClientConn struct {
|
||||
br *bufio.Reader
|
||||
fr *http2Framer
|
||||
lastActive time.Time
|
||||
|
||||
// Settings from peer:
|
||||
// Settings from peer: (also guarded by mu)
|
||||
maxFrameSize uint32
|
||||
maxConcurrentStreams uint32
|
||||
initialWindowSize uint32
|
||||
hbuf bytes.Buffer // HPACK encoder writes into this
|
||||
henc *hpack.Encoder
|
||||
freeBuf [][]byte
|
||||
|
||||
hbuf bytes.Buffer // HPACK encoder writes into this
|
||||
henc *hpack.Encoder
|
||||
freeBuf [][]byte
|
||||
|
||||
wmu sync.Mutex // held while writing; acquire AFTER mu if holding both
|
||||
werr error // first write error that has occurred
|
||||
@@ -5244,10 +5262,6 @@ func (t *http2Transport) NewClientConn(c net.Conn) (*http2ClientConn, error) {
|
||||
}
|
||||
|
||||
func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2ClientConn, error) {
|
||||
if http2VerboseLogs {
|
||||
t.vlogf("http2: Transport creating client conn to %v", c.RemoteAddr())
|
||||
}
|
||||
|
||||
cc := &http2ClientConn{
|
||||
t: t,
|
||||
tconn: c,
|
||||
@@ -5260,6 +5274,10 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
|
||||
singleUse: singleUse,
|
||||
wantSettingsAck: true,
|
||||
}
|
||||
if http2VerboseLogs {
|
||||
t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
|
||||
}
|
||||
|
||||
cc.cond = sync.NewCond(&cc.mu)
|
||||
cc.flow.add(int32(http2initialWindowSize))
|
||||
|
||||
@@ -5324,7 +5342,7 @@ func (cc *http2ClientConn) canTakeNewRequestLocked() bool {
|
||||
}
|
||||
return cc.goAway == nil && !cc.closed &&
|
||||
int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) &&
|
||||
cc.nextStreamID < 2147483647
|
||||
cc.nextStreamID < math.MaxInt32
|
||||
}
|
||||
|
||||
func (cc *http2ClientConn) closeIfIdle() {
|
||||
@@ -5334,9 +5352,13 @@ func (cc *http2ClientConn) closeIfIdle() {
|
||||
return
|
||||
}
|
||||
cc.closed = true
|
||||
nextID := cc.nextStreamID
|
||||
|
||||
cc.mu.Unlock()
|
||||
|
||||
if http2VerboseLogs {
|
||||
cc.vlogf("http2: Transport closing idle conn %p (forSingleUse=%v, maxStream=%v)", cc, cc.singleUse, nextID-2)
|
||||
}
|
||||
cc.tconn.Close()
|
||||
}
|
||||
|
||||
@@ -5438,12 +5460,15 @@ func http2bodyAndLength(req *Request) (body io.Reader, contentLen int64) {
|
||||
// We have a body but a zero content length. Test to see if
|
||||
// it's actually zero or just unset.
|
||||
var buf [1]byte
|
||||
n, rerr := io.ReadFull(body, buf[:])
|
||||
n, rerr := body.Read(buf[:])
|
||||
if rerr != nil && rerr != io.EOF {
|
||||
return http2errorReader{rerr}, -1
|
||||
}
|
||||
if n == 1 {
|
||||
|
||||
if rerr == io.EOF {
|
||||
return bytes.NewReader(buf[:]), 1
|
||||
}
|
||||
return io.MultiReader(bytes.NewReader(buf[:]), body), -1
|
||||
}
|
||||
|
||||
@@ -5692,8 +5717,13 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos
|
||||
}
|
||||
}
|
||||
|
||||
if sentEnd {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var trls []byte
|
||||
if !sentEnd && hasTrailers {
|
||||
if hasTrailers {
|
||||
cc.mu.Lock()
|
||||
defer cc.mu.Unlock()
|
||||
trls = cc.encodeTrailers(req)
|
||||
@@ -5986,11 +6016,15 @@ func (rl *http2clientConnReadLoop) run() error {
|
||||
for {
|
||||
f, err := cc.fr.ReadFrame()
|
||||
if err != nil {
|
||||
cc.vlogf("Transport readFrame error: (%T) %v", err, err)
|
||||
cc.vlogf("http2: Transport readFrame error on conn %p: (%T) %v", cc, err, err)
|
||||
}
|
||||
if se, ok := err.(http2StreamError); ok {
|
||||
if cs := cc.streamByID(se.StreamID, true); cs != nil {
|
||||
rl.endStreamError(cs, cc.fr.errDetail)
|
||||
cs.cc.writeStreamReset(cs.ID, se.Code, err)
|
||||
if se.Cause == nil {
|
||||
se.Cause = cc.fr.errDetail
|
||||
}
|
||||
rl.endStreamError(cs, se)
|
||||
}
|
||||
continue
|
||||
} else if err != nil {
|
||||
@@ -6034,6 +6068,9 @@ func (rl *http2clientConnReadLoop) run() error {
|
||||
cc.logf("Transport: unhandled response frame type %T", f)
|
||||
}
|
||||
if err != nil {
|
||||
if http2VerboseLogs {
|
||||
cc.vlogf("http2: Transport conn %p received error from processing frame %v: %v", cc, http2summarizeFrame(f), err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if rl.closeWhenIdle && gotReply && maybeIdle && len(rl.activeRes) == 0 {
|
||||
@@ -6381,6 +6418,11 @@ func (rl *http2clientConnReadLoop) endStreamError(cs *http2clientStream, err err
|
||||
if http2isConnectionCloseRequest(cs.req) {
|
||||
rl.closeWhenIdle = true
|
||||
}
|
||||
|
||||
select {
|
||||
case cs.resc <- http2resAndError{err: err}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *http2clientStream) copyTrailers() {
|
||||
@@ -6425,6 +6467,16 @@ func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error
|
||||
cc.maxConcurrentStreams = s.Val
|
||||
case http2SettingInitialWindowSize:
|
||||
|
||||
if s.Val > math.MaxInt32 {
|
||||
return http2ConnectionError(http2ErrCodeFlowControl)
|
||||
}
|
||||
|
||||
delta := int32(s.Val) - int32(cc.initialWindowSize)
|
||||
for _, cs := range cc.streams {
|
||||
cs.flow.add(delta)
|
||||
}
|
||||
cc.cond.Broadcast()
|
||||
|
||||
cc.initialWindowSize = s.Val
|
||||
default:
|
||||
|
||||
@@ -6475,7 +6527,7 @@ func (rl *http2clientConnReadLoop) processResetStream(f *http2RSTStreamFrame) er
|
||||
case <-cs.peerReset:
|
||||
|
||||
default:
|
||||
err := http2StreamError{cs.ID, f.ErrCode}
|
||||
err := http2streamError(cs.ID, f.ErrCode)
|
||||
cs.resetErr = err
|
||||
close(cs.peerReset)
|
||||
cs.bufPipe.CloseWithError(err)
|
||||
|
||||
@@ -398,6 +398,15 @@ func (t *Transport) RoundTrip(req *Request) (*Response, error) {
|
||||
// HTTP request on a new connection. The non-nil input error is the
|
||||
// error from roundTrip.
|
||||
func (pc *persistConn) shouldRetryRequest(req *Request, err error) bool {
|
||||
if err == http2ErrNoCachedConn {
|
||||
// Issue 16582: if the user started a bunch of
|
||||
// requests at once, they can all pick the same conn
|
||||
// and violate the server's max concurrent streams.
|
||||
// Instead, match the HTTP/1 behavior for now and dial
|
||||
// again to get a new TCP connection, rather than failing
|
||||
// this request.
|
||||
return true
|
||||
}
|
||||
if err == errMissingHost {
|
||||
// User error.
|
||||
return false
|
||||
@@ -489,8 +498,9 @@ func (t *Transport) CloseIdleConnections() {
|
||||
// CancelRequest cancels an in-flight request by closing its connection.
|
||||
// CancelRequest should only be called after RoundTrip has returned.
|
||||
//
|
||||
// Deprecated: Use Request.Cancel instead. CancelRequest cannot cancel
|
||||
// HTTP/2 requests.
|
||||
// Deprecated: Use Request.WithContext to create a request with a
|
||||
// cancelable context instead. CancelRequest cannot cancel HTTP/2
|
||||
// requests.
|
||||
func (t *Transport) CancelRequest(req *Request) {
|
||||
t.reqMu.Lock()
|
||||
cancel := t.reqCanceler[req]
|
||||
@@ -580,6 +590,7 @@ var (
|
||||
errReadLoopExiting = errors.New("http: persistConn.readLoop exiting")
|
||||
errServerClosedIdle = errors.New("http: server closed idle connection")
|
||||
errIdleConnTimeout = errors.New("http: idle connection timeout")
|
||||
errNotCachingH2Conn = errors.New("http: not caching alternate protocol's connections")
|
||||
)
|
||||
|
||||
// transportReadFromServerError is used by Transport.readLoop when the
|
||||
@@ -623,6 +634,9 @@ func (t *Transport) tryPutIdleConn(pconn *persistConn) error {
|
||||
if pconn.isBroken() {
|
||||
return errConnBroken
|
||||
}
|
||||
if pconn.alt != nil {
|
||||
return errNotCachingH2Conn
|
||||
}
|
||||
pconn.markReused()
|
||||
key := pconn.cacheKey
|
||||
|
||||
|
||||
@@ -3511,6 +3511,61 @@ func TestTransportIdleConnTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 16208: Go 1.7 crashed after Transport.IdleConnTimeout if an
|
||||
// HTTP/2 connection was established but but its caller no longer
|
||||
// wanted it. (Assuming the connection cache was enabled, which it is
|
||||
// by default)
|
||||
//
|
||||
// This test reproduced the crash by setting the IdleConnTimeout low
|
||||
// (to make the test reasonable) and then making a request which is
|
||||
// canceled by the DialTLS hook, which then also waits to return the
|
||||
// real connection until after the RoundTrip saw the error. Then we
|
||||
// know the successful tls.Dial from DialTLS will need to go into the
|
||||
// idle pool. Then we give it a of time to explode.
|
||||
func TestIdleConnH2Crash(t *testing.T) {
|
||||
cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||
// nothing
|
||||
}))
|
||||
defer cst.close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
gotErr := make(chan bool, 1)
|
||||
|
||||
cst.tr.IdleConnTimeout = 5 * time.Millisecond
|
||||
cst.tr.DialTLS = func(network, addr string) (net.Conn, error) {
|
||||
cancel()
|
||||
<-gotErr
|
||||
c, err := tls.Dial(network, addr, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h2"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
if cs := c.ConnectionState(); cs.NegotiatedProtocol != "h2" {
|
||||
t.Errorf("protocol = %q; want %q", cs.NegotiatedProtocol, "h2")
|
||||
c.Close()
|
||||
return nil, errors.New("bogus")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
req, _ := NewRequest("GET", cst.ts.URL, nil)
|
||||
req = req.WithContext(ctx)
|
||||
res, err := cst.c.Do(req)
|
||||
if err == nil {
|
||||
res.Body.Close()
|
||||
t.Fatal("unexpected success")
|
||||
}
|
||||
gotErr <- true
|
||||
|
||||
// Wait for the explosion.
|
||||
time.Sleep(cst.tr.IdleConnTimeout * 10)
|
||||
}
|
||||
|
||||
type funcConn struct {
|
||||
net.Conn
|
||||
read func([]byte) (int, error)
|
||||
|
||||
@@ -28,6 +28,12 @@ func (p *Process) blockUntilWaitable() (bool, error) {
|
||||
_, _, e := syscall.Syscall6(syscall.SYS_WAITID, _P_PID, uintptr(p.Pid), uintptr(unsafe.Pointer(psig)), syscall.WEXITED|syscall.WNOWAIT, 0, 0)
|
||||
runtime.KeepAlive(psig)
|
||||
if e != 0 {
|
||||
// waitid has been available since Linux 2.6.9, but
|
||||
// reportedly is not available in Ubuntu on Windows.
|
||||
// See issue 16610.
|
||||
if e == syscall.ENOSYS {
|
||||
return false, nil
|
||||
}
|
||||
return false, NewSyscallError("waitid", e)
|
||||
}
|
||||
return true, nil
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
|
||||
package filepath
|
||||
|
||||
var ToNorm = toNorm
|
||||
var (
|
||||
ToNorm = toNorm
|
||||
NormBase = normBase
|
||||
)
|
||||
|
||||
@@ -840,7 +840,7 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
if p, err := filepath.EvalSymlinks(path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(dest) {
|
||||
t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
|
||||
}
|
||||
|
||||
// test EvalSymlinks(".")
|
||||
@@ -872,6 +872,34 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
t.Errorf(`EvalSymlinks(".") in %q directory returns %q, want "." or %q`, d.path, p, want)
|
||||
}()
|
||||
|
||||
// test EvalSymlinks(".."+path)
|
||||
func() {
|
||||
defer func() {
|
||||
err := os.Chdir(wd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
err := os.Chdir(simpleJoin(tmpDir, "test"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
path := simpleJoin("..", d.path)
|
||||
dest := simpleJoin("..", d.dest)
|
||||
if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) {
|
||||
dest = d.dest
|
||||
}
|
||||
|
||||
if p, err := filepath.EvalSymlinks(path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(dest) {
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", path, p, dest)
|
||||
}
|
||||
}()
|
||||
|
||||
// test EvalSymlinks where parameter is relative path
|
||||
func() {
|
||||
defer func() {
|
||||
@@ -889,7 +917,7 @@ func TestEvalSymlinks(t *testing.T) {
|
||||
if p, err := filepath.EvalSymlinks(d.path); err != nil {
|
||||
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
|
||||
} else if filepath.Clean(p) != filepath.Clean(d.dest) {
|
||||
t.Errorf("Clean(%q)=%q, want %q", d.path, p, d.dest)
|
||||
t.Errorf("EvalSymlinks(%q)=%q, want %q", d.path, p, d.dest)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -329,9 +329,106 @@ func TestToNorm(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
got, err := filepath.ToNorm(test.arg, stubBase)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected toNorm error, arg: %s, err: %v\n", test.arg, err)
|
||||
t.Errorf("toNorm(%s) failed: %v\n", test.arg, err)
|
||||
} else if got != test.want {
|
||||
t.Errorf("toNorm error, arg: %s, want: %s, got: %s\n", test.arg, test.want, got)
|
||||
t.Errorf("toNorm(%s) returns %s, but %s expected\n", test.arg, got, test.want)
|
||||
}
|
||||
}
|
||||
|
||||
testPath := `{{tmp}}\test\foo\bar`
|
||||
|
||||
testsDir := []struct {
|
||||
wd string
|
||||
arg string
|
||||
want string
|
||||
}{
|
||||
// test absolute paths
|
||||
{".", `{{tmp}}\test\foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\.\test/foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\test\..\test\foo\bar`, `{{tmp}}\test\foo\bar`},
|
||||
{".", `{{tmp}}\TEST\FOO\BAR`, `{{tmp}}\test\foo\bar`},
|
||||
|
||||
// test relative paths begin with drive letter
|
||||
{`{{tmp}}\test`, `{{tmpvol}}.`, `{{tmpvol}}.`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}..`, `{{tmpvol}}..`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}.\foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}foo\..\foo\bar`, `{{tmpvol}}foo\bar`},
|
||||
{`{{tmp}}\test`, `{{tmpvol}}FOO\BAR`, `{{tmpvol}}foo\bar`},
|
||||
|
||||
// test relative paths begin with '\'
|
||||
{".", `{{tmpnovol}}\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\.\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\test\..\test\foo\bar`, `{{tmpnovol}}\test\foo\bar`},
|
||||
{".", `{{tmpnovol}}\TEST\FOO\BAR`, `{{tmpnovol}}\test\foo\bar`},
|
||||
|
||||
// test relative paths begin without '\'
|
||||
{`{{tmp}}\test`, ".", `.`},
|
||||
{`{{tmp}}\test`, "..", `..`},
|
||||
{`{{tmp}}\test`, `foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `.\foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `foo\..\foo\bar`, `foo\bar`},
|
||||
{`{{tmp}}\test`, `FOO\BAR`, `foo\bar`},
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.Chdir(cwd)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
tmp, err := ioutil.TempDir("", "testToNorm")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
// ioutil.TempDir might return "non-canonical" name.
|
||||
tmp, err = filepath.EvalSymlinks(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll(strings.Replace(testPath, "{{tmp}}", tmp, -1), 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tmpVol := filepath.VolumeName(tmp)
|
||||
tmpNoVol := tmp[len(tmpVol):]
|
||||
|
||||
for _, test := range testsDir {
|
||||
wd := strings.Replace(strings.Replace(strings.Replace(test.wd, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
arg := strings.Replace(strings.Replace(strings.Replace(test.arg, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
want := strings.Replace(strings.Replace(strings.Replace(test.want, "{{tmp}}", tmp, -1), "{{tmpvol}}", tmpVol, -1), "{{tmpnovol}}", tmpNoVol, -1)
|
||||
|
||||
if test.wd == "." {
|
||||
err := os.Chdir(cwd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
err := os.Chdir(wd)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
got, err := filepath.ToNorm(arg, filepath.NormBase)
|
||||
if err != nil {
|
||||
t.Errorf("toNorm(%s) failed: %v (wd=%s)\n", arg, err, wd)
|
||||
} else if got != want {
|
||||
t.Errorf("toNorm(%s) returns %s, but %s expected (wd=%s)\n", arg, got, want, wd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func normVolumeName(path string) string {
|
||||
return strings.ToUpper(volume)
|
||||
}
|
||||
|
||||
// normBase retruns the last element of path.
|
||||
// normBase returns the last element of path with correct case.
|
||||
func normBase(path string) (string, error) {
|
||||
p, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
@@ -40,7 +40,24 @@ func normBase(path string) (string, error) {
|
||||
return syscall.UTF16ToString(data.FileName[:]), nil
|
||||
}
|
||||
|
||||
func toNorm(path string, base func(string) (string, error)) (string, error) {
|
||||
// baseIsDotDot returns whether the last element of path is "..".
|
||||
// The given path should be 'Clean'-ed in advance.
|
||||
func baseIsDotDot(path string) bool {
|
||||
i := strings.LastIndexByte(path, Separator)
|
||||
return path[i+1:] == ".."
|
||||
}
|
||||
|
||||
// toNorm returns the normalized path that is guranteed to be unique.
|
||||
// It should accept the following formats:
|
||||
// * UNC paths (e.g \\server\share\foo\bar)
|
||||
// * absolute paths (e.g C:\foo\bar)
|
||||
// * relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.)
|
||||
// * relative paths begin with '\' (e.g \foo\bar)
|
||||
// * relative paths begin without '\' (e.g foo\bar, ..\foo\bar, .., .)
|
||||
// The returned normalized path will be in the same form (of 5 listed above) as the input path.
|
||||
// If two paths A and B are indicating the same file with the same format, toNorm(A) should be equal to toNorm(B).
|
||||
// The normBase parameter should be equal to the normBase func, except for in tests. See docs on the normBase func.
|
||||
func toNorm(path string, normBase func(string) (string, error)) (string, error) {
|
||||
if path == "" {
|
||||
return path, nil
|
||||
}
|
||||
@@ -58,7 +75,13 @@ func toNorm(path string, base func(string) (string, error)) (string, error) {
|
||||
var normPath string
|
||||
|
||||
for {
|
||||
name, err := base(volume + path)
|
||||
if baseIsDotDot(path) {
|
||||
normPath = path + `\` + normPath
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
name, err := normBase(volume + path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -5720,6 +5720,8 @@ func TestTypeStrings(t *testing.T) {
|
||||
{TypeOf(new(XM)), "*reflect_test.XM"},
|
||||
{TypeOf(new(XM).String), "func() string"},
|
||||
{TypeOf(new(XM)).Method(0).Type, "func(*reflect_test.XM) string"},
|
||||
{ChanOf(3, TypeOf(XM{})), "chan reflect_test.XM"},
|
||||
{MapOf(TypeOf(int(0)), TypeOf(XM{})), "map[int]reflect_test.XM"},
|
||||
}
|
||||
|
||||
for i, test := range stringTests {
|
||||
|
||||
@@ -1848,6 +1848,7 @@ func ChanOf(dir ChanDir, t Type) Type {
|
||||
prototype := *(**chanType)(unsafe.Pointer(&ichan))
|
||||
ch := new(chanType)
|
||||
*ch = *prototype
|
||||
ch.tflag = 0
|
||||
ch.dir = uintptr(dir)
|
||||
ch.str = resolveReflectName(newName(s, "", "", false))
|
||||
ch.hash = fnv1(typ.hash, 'c', byte(dir))
|
||||
@@ -1892,6 +1893,7 @@ func MapOf(key, elem Type) Type {
|
||||
mt := new(mapType)
|
||||
*mt = **(**mapType)(unsafe.Pointer(&imap))
|
||||
mt.str = resolveReflectName(newName(s, "", "", false))
|
||||
mt.tflag = 0
|
||||
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
|
||||
mt.key = ktyp
|
||||
mt.elem = etyp
|
||||
|
||||
@@ -917,7 +917,9 @@ func scavengelist(list *mSpanList, now, limit uint64) uintptr {
|
||||
// more memory than we want.)
|
||||
start = (start + sys.PhysPageSize - 1) &^ (sys.PhysPageSize - 1)
|
||||
end &^= sys.PhysPageSize - 1
|
||||
if start == end {
|
||||
if end <= start {
|
||||
// start and end don't span a
|
||||
// whole physical page.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,18 @@ TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
|
||||
|
||||
// When building with -buildmode=c-shared, this symbol is called when the shared
|
||||
// library is loaded.
|
||||
TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0x48
|
||||
// Note: This function calls external C code, which might required 16-byte stack
|
||||
// alignment after cmd/internal/obj applies its transformations.
|
||||
TEXT _rt0_amd64_linux_lib(SB),NOSPLIT,$0x50
|
||||
MOVQ SP, AX
|
||||
ANDQ $-16, SP
|
||||
MOVQ BX, 0x10(SP)
|
||||
MOVQ BP, 0x18(SP)
|
||||
MOVQ R12, 0x20(SP)
|
||||
MOVQ R13, 0x28(SP)
|
||||
MOVQ R14, 0x30(SP)
|
||||
MOVQ R15, 0x38(SP)
|
||||
MOVQ AX, 0x40(SP)
|
||||
|
||||
MOVQ DI, _rt0_amd64_linux_lib_argc<>(SB)
|
||||
MOVQ SI, _rt0_amd64_linux_lib_argv<>(SB)
|
||||
@@ -50,6 +55,7 @@ restore:
|
||||
MOVQ 0x28(SP), R13
|
||||
MOVQ 0x30(SP), R14
|
||||
MOVQ 0x38(SP), R15
|
||||
MOVQ 0x40(SP), SP
|
||||
RET
|
||||
|
||||
TEXT _rt0_amd64_linux_lib_go(SB),NOSPLIT,$0
|
||||
|
||||
@@ -162,11 +162,15 @@ TEXT runtime·mincore(SB),NOSPLIT,$0
|
||||
TEXT time·now(SB), 7, $32
|
||||
MOVW $8(R13), R0 // timeval
|
||||
MOVW $0, R1 // zone
|
||||
MOVW $0, R2 // see issue 16570
|
||||
MOVW $SYS_gettimeofday, R12
|
||||
SWI $0x80 // Note: R0 is tv_sec, R1 is tv_usec
|
||||
|
||||
CMP $0, R0
|
||||
BNE inreg
|
||||
MOVW 8(R13), R0
|
||||
MOVW 12(R13), R1
|
||||
inreg:
|
||||
MOVW R1, R2 // usec
|
||||
|
||||
MOVW R0, sec+0(FP)
|
||||
MOVW $0, R1
|
||||
MOVW R1, loc+4(FP)
|
||||
@@ -178,9 +182,14 @@ TEXT time·now(SB), 7, $32
|
||||
TEXT runtime·nanotime(SB),NOSPLIT,$32
|
||||
MOVW $8(R13), R0 // timeval
|
||||
MOVW $0, R1 // zone
|
||||
MOVW $0, R2 // see issue 16570
|
||||
MOVW $SYS_gettimeofday, R12
|
||||
SWI $0x80 // Note: R0 is tv_sec, R1 is tv_usec
|
||||
|
||||
CMP $0, R0
|
||||
BNE inreg
|
||||
MOVW 8(R13), R0
|
||||
MOVW 12(R13), R1
|
||||
inreg:
|
||||
MOVW R1, R2
|
||||
MOVW $1000000000, R3
|
||||
MULLU R0, R3, (R1, R0)
|
||||
|
||||
@@ -155,9 +155,14 @@ TEXT time·now(SB),NOSPLIT,$40-12
|
||||
MOVD RSP, R0 // timeval
|
||||
MOVD R0, R9 // this is how dyld calls gettimeofday
|
||||
MOVW $0, R1 // zone
|
||||
MOVD $0, R2 // see issue 16570
|
||||
MOVW $SYS_gettimeofday, R16
|
||||
SVC $0x80 // Note: x0 is tv_sec, w1 is tv_usec
|
||||
|
||||
CMP $0, R0
|
||||
BNE inreg
|
||||
MOVD 0(RSP), R0
|
||||
MOVW 8(RSP), R1
|
||||
inreg:
|
||||
MOVD R0, sec+0(FP)
|
||||
MOVW $1000, R3
|
||||
MUL R3, R1
|
||||
@@ -168,9 +173,14 @@ TEXT runtime·nanotime(SB),NOSPLIT,$40
|
||||
MOVD RSP, R0 // timeval
|
||||
MOVD R0, R9 // this is how dyld calls gettimeofday
|
||||
MOVW $0, R1 // zone
|
||||
MOVD $0, R2 // see issue 16570
|
||||
MOVW $SYS_gettimeofday, R16
|
||||
SVC $0x80 // Note: x0 is tv_sec, w1 is tv_usec
|
||||
|
||||
CMP $0, R0
|
||||
BNE inreg
|
||||
MOVD 0(RSP), R0
|
||||
MOVW 8(RSP), R1
|
||||
inreg:
|
||||
MOVW $1000000000, R3
|
||||
MUL R3, R0
|
||||
MOVW $1000, R3
|
||||
|
||||
@@ -7,28 +7,45 @@
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const MNT_WAIT = 1
|
||||
const MNT_NOWAIT = 2
|
||||
|
||||
func TestGetfsstat(t *testing.T) {
|
||||
n, err := syscall.Getfsstat(nil, MNT_WAIT)
|
||||
const flags = MNT_NOWAIT // see Issue 16937
|
||||
n, err := syscall.Getfsstat(nil, flags)
|
||||
t.Logf("Getfsstat(nil, %d) = (%v, %v)", flags, n, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data := make([]syscall.Statfs_t, n)
|
||||
n, err = syscall.Getfsstat(data, MNT_WAIT)
|
||||
n2, err := syscall.Getfsstat(data, flags)
|
||||
t.Logf("Getfsstat([]syscall.Statfs_t, %d) = (%v, %v)", flags, n2, err)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
empty := syscall.Statfs_t{}
|
||||
for _, stat := range data {
|
||||
if stat == empty {
|
||||
t.Fatal("an empty Statfs_t struct was returned")
|
||||
if n != n2 {
|
||||
t.Errorf("Getfsstat(nil) = %d, but subsequent Getfsstat(slice) = %d", n, n2)
|
||||
}
|
||||
for i, stat := range data {
|
||||
if stat == (syscall.Statfs_t{}) {
|
||||
t.Errorf("index %v is an empty Statfs_t struct", i)
|
||||
}
|
||||
}
|
||||
if t.Failed() {
|
||||
for i, stat := range data[:n2] {
|
||||
t.Logf("data[%v] = %+v", i, stat)
|
||||
}
|
||||
mount, err := exec.Command("mount").CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("mount: %v\n%s", err, mount)
|
||||
} else {
|
||||
t.Logf("mount: %s", mount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,21 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
|
||||
}
|
||||
|
||||
//sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, err error)
|
||||
func Gettimeofday(tv *Timeval) (err error) {
|
||||
// The tv passed to gettimeofday must be non-nil
|
||||
// but is otherwise unused. The answers come back
|
||||
// in the two registers.
|
||||
func Gettimeofday(tv *Timeval) error {
|
||||
// The tv passed to gettimeofday must be non-nil.
|
||||
// Before macOS Sierra (10.12), tv was otherwise unused and
|
||||
// the answers came back in the two registers.
|
||||
// As of Sierra, gettimeofday return zeros and populates
|
||||
// tv itself.
|
||||
sec, usec, err := gettimeofday(tv)
|
||||
tv.Sec = int32(sec)
|
||||
tv.Usec = int32(usec)
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sec != 0 || usec != 0 {
|
||||
tv.Sec = int32(sec)
|
||||
tv.Usec = int32(usec)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
|
||||
@@ -26,14 +26,21 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
|
||||
}
|
||||
|
||||
//sysnb gettimeofday(tp *Timeval) (sec int64, usec int32, err error)
|
||||
func Gettimeofday(tv *Timeval) (err error) {
|
||||
// The tv passed to gettimeofday must be non-nil
|
||||
// but is otherwise unused. The answers come back
|
||||
// in the two registers.
|
||||
func Gettimeofday(tv *Timeval) error {
|
||||
// The tv passed to gettimeofday must be non-nil.
|
||||
// Before macOS Sierra (10.12), tv was otherwise unused and
|
||||
// the answers came back in the two registers.
|
||||
// As of Sierra, gettimeofday return zeros and populates
|
||||
// tv itself.
|
||||
sec, usec, err := gettimeofday(tv)
|
||||
tv.Sec = sec
|
||||
tv.Usec = usec
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sec != 0 || usec != 0 {
|
||||
tv.Sec = sec
|
||||
tv.Usec = usec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
|
||||
@@ -26,14 +26,19 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
|
||||
}
|
||||
|
||||
//sysnb gettimeofday(tp *Timeval) (sec int32, usec int32, err error)
|
||||
func Gettimeofday(tv *Timeval) (err error) {
|
||||
func Gettimeofday(tv *Timeval) error {
|
||||
// The tv passed to gettimeofday must be non-nil
|
||||
// but is otherwise unused. The answers come back
|
||||
// in the two registers.
|
||||
sec, usec, err := gettimeofday(tv)
|
||||
tv.Sec = int32(sec)
|
||||
tv.Usec = int32(usec)
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sec != 0 || usec != 0 {
|
||||
tv.Sec = int32(sec)
|
||||
tv.Usec = int32(usec)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
|
||||
@@ -26,14 +26,19 @@ func NsecToTimeval(nsec int64) (tv Timeval) {
|
||||
}
|
||||
|
||||
//sysnb gettimeofday(tp *Timeval) (sec int64, usec int32, err error)
|
||||
func Gettimeofday(tv *Timeval) (err error) {
|
||||
func Gettimeofday(tv *Timeval) error {
|
||||
// The tv passed to gettimeofday must be non-nil
|
||||
// but is otherwise unused. The answers come back
|
||||
// in the two registers.
|
||||
sec, usec, err := gettimeofday(tv)
|
||||
tv.Sec = sec
|
||||
tv.Usec = usec
|
||||
return err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if sec != 0 || usec != 0 {
|
||||
tv.Sec = sec
|
||||
tv.Usec = usec
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetKevent(k *Kevent_t, fd, mode, flags int) {
|
||||
|
||||
23
src/syscall/syscall_darwin_test.go
Normal file
23
src/syscall/syscall_darwin_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin
|
||||
// +build amd64 386 arm arm64
|
||||
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDarwinGettimeofday(t *testing.T) {
|
||||
tv := &syscall.Timeval{}
|
||||
if err := syscall.Gettimeofday(tv); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tv.Sec == 0 && tv.Usec == 0 {
|
||||
t.Fatal("Sec and Usec both zero")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user