29
29
public final class FeedableBodyGenerator implements BodyGenerator {
30
30
private final static byte [] END_PADDING = "\r \n " .getBytes (US_ASCII );
31
31
private final static byte [] ZERO = "0" .getBytes (US_ASCII );
32
+ private final static ByteBuffer EMPTY_BUFFER = ByteBuffer .allocate (0 );
32
33
private final Queue <BodyPart > queue = new ConcurrentLinkedQueue <>();
33
34
private FeedListener listener ;
34
35
@@ -60,7 +61,7 @@ public void writeChunkBoundaries() {
60
61
}
61
62
62
63
private enum PushBodyState {
63
- ONGOING , CLOSING , FINISHED ;
64
+ ONGOING , FINISHED ;
64
65
}
65
66
66
67
public final class PushBody implements Body {
@@ -74,51 +75,46 @@ public long getContentLength() {
74
75
75
76
@ Override
76
77
public long read (final ByteBuffer buffer ) throws IOException {
77
- BodyPart nextPart = queue .peek ();
78
- if (nextPart == null ) {
79
- // Nothing in the queue
80
- switch (state ) {
78
+ switch (state ) {
81
79
case ONGOING :
82
- return 0 ;
83
- case CLOSING :
84
- buffer .put (ZERO );
85
- buffer .put (END_PADDING );
86
- buffer .put (END_PADDING );
87
- state = PushBodyState .FINISHED ;
88
- return buffer .position ();
80
+ return readNextPart (buffer );
89
81
case FINISHED :
90
82
return -1 ;
91
- }
92
- }
93
- if (nextPart .buffer .remaining () == 0 ) {
94
- // skip empty buffers
95
- // if we return 0 here it would suspend the stream - we don't want that
96
- queue .remove ();
97
- if (nextPart .isLast ) {
98
- state = writeChunkBoundaries ? PushBodyState .CLOSING : PushBodyState .FINISHED ;
99
- }
100
- return read (buffer );
83
+ default :
84
+ throw new IllegalStateException ("Illegal process state." );
101
85
}
102
- int capacity = buffer .remaining () - 10 ; // be safe (we'll have to add size, ending, etc.)
103
- int size = Math .min (nextPart .buffer .remaining (), capacity );
104
- if (size != 0 ) {
105
- if (writeChunkBoundaries ) {
106
- buffer .put (Integer .toHexString (size ).getBytes (US_ASCII ));
107
- buffer .put (END_PADDING );
108
- }
109
- for (int i = 0 ; i < size ; i ++) {
110
- buffer .put (nextPart .buffer .get ());
86
+ }
87
+
88
+ private long readNextPart (ByteBuffer buffer ) throws IOException {
89
+ int reads = 0 ;
90
+ while (buffer .hasRemaining () && state != PushBodyState .FINISHED ) {
91
+ BodyPart nextPart = queue .peek ();
92
+ if (nextPart == null ) {
93
+ // Nothing in the queue. suspend stream if nothing was read. (reads == 0)
94
+ return reads ;
95
+ } else if (!nextPart .buffer .hasRemaining () && !nextPart .isLast ) {
96
+ // skip empty buffers
97
+ queue .remove ();
98
+ } else {
99
+ readBodyPart (buffer , nextPart );
100
+ reads ++;
111
101
}
112
- if (writeChunkBoundaries )
113
- buffer .put (END_PADDING );
114
102
}
115
- if (!nextPart .buffer .hasRemaining ()) {
116
- if (nextPart .isLast ) {
117
- state = writeChunkBoundaries ? PushBodyState .CLOSING : PushBodyState .FINISHED ;
103
+ return reads ;
104
+ }
105
+
106
+ private void readBodyPart (ByteBuffer buffer , BodyPart part ) {
107
+ part .initBoundaries ();
108
+ move (buffer , part .size );
109
+ move (buffer , part .buffer );
110
+ move (buffer , part .endPadding );
111
+
112
+ if (!part .buffer .hasRemaining () && !part .endPadding .hasRemaining ()) {
113
+ if (part .isLast ) {
114
+ state = PushBodyState .FINISHED ;
118
115
}
119
116
queue .remove ();
120
117
}
121
- return size ;
122
118
}
123
119
124
120
@ Override
@@ -127,13 +123,56 @@ public void close() {
127
123
128
124
}
129
125
130
- private final static class BodyPart {
126
+
127
+ private void move (ByteBuffer destination , ByteBuffer source ) {
128
+ while (destination .hasRemaining () && source .hasRemaining ()) {
129
+ destination .put (source .get ());
130
+ }
131
+ }
132
+
133
+ private final class BodyPart {
131
134
private final boolean isLast ;
135
+ private ByteBuffer size = null ;
132
136
private final ByteBuffer buffer ;
137
+ private ByteBuffer endPadding = null ;
133
138
134
139
public BodyPart (final ByteBuffer buffer , final boolean isLast ) {
135
140
this .buffer = buffer ;
136
141
this .isLast = isLast ;
137
142
}
143
+
144
+ private void initBoundaries () {
145
+ if (size == null && endPadding == null ) {
146
+ if (FeedableBodyGenerator .this .writeChunkBoundaries ) {
147
+ if (buffer .hasRemaining ()) {
148
+ final byte [] sizeAsHex = Integer .toHexString (buffer .remaining ()).getBytes (US_ASCII );
149
+ size = ByteBuffer .allocate (sizeAsHex .length + END_PADDING .length );
150
+ size .put (sizeAsHex );
151
+ size .put (END_PADDING );
152
+ size .flip ();
153
+ } else {
154
+ size = EMPTY_BUFFER ;
155
+ }
156
+
157
+ if (isLast ) {
158
+ endPadding = ByteBuffer .allocate (END_PADDING .length * 3 + ZERO .length );
159
+ if (buffer .hasRemaining ()) {
160
+ endPadding .put (END_PADDING );
161
+ }
162
+
163
+ //add last empty
164
+ endPadding .put (ZERO );
165
+ endPadding .put (END_PADDING );
166
+ endPadding .put (END_PADDING );
167
+ endPadding .flip ();
168
+ } else {
169
+ endPadding = ByteBuffer .wrap (END_PADDING );
170
+ }
171
+ } else {
172
+ size = EMPTY_BUFFER ;
173
+ endPadding = EMPTY_BUFFER ;
174
+ }
175
+ }
176
+ }
138
177
}
139
178
}
0 commit comments