// Returns the request offset into the builder buffer public UInt32 Build(ByteBuilder builder, HttpClient client, Boolean keepAlive) { if (method == null) { throw new InvalidOperationException("The HttpRequest method must be set"); } // // <METHOD> <resource> HTTP/1.1\r\n // builder.AppendAscii(method); // Todo: Should I verify that method is ascii beforehand? builder.AppendAscii(' '); if (!String.IsNullOrEmpty(resource)) { builder.AppendAscii(resource); // Todo: Should I verify that resource is ascii beforehand? } else if (resourceAppender != null) { resourceAppender(builder); } else { throw new InvalidOperationException("The HttpRequest resource must be set"); } builder.Append(VersionPart); // // Host: <host>[:<port>] // builder.Append(Http.HostHeaderPrefix); if (overrideHostHeader != null) { builder.AppendAscii(overrideHostHeader); } else { builder.AppendAscii(client.IPOrHost); if (client.Port != 80 || forcePortInHostHeader) { builder.AppendAscii(':'); builder.AppendNumber(client.Port, 10); } } builder.Append(Http.Newline); // // Header: Content-Length // // TODO: when do I not need a Content-Length? UInt32 contentLengthOffset = UInt32.MaxValue; { Boolean hasContent; UInt32 contentLength; if (content.contentAsBytes != null) { hasContent = true; contentLength = (UInt32)content.contentAsBytes.Length; } else if (content.contentAsString != null) { hasContent = true; contentLength = content.contentAsStringEncoder.GetEncodeLength(content.contentAsString); } else if (content.contentAppender != null) { hasContent = true; contentLength = UInt32.MaxValue; // Placeholder } else { hasContent = false; contentLength = 0; } if (hasContent) { builder.Append(Http.ContentLengthHeaderPrefix); contentLengthOffset = builder.contentLength; builder.AppendNumber(contentLength); builder.Append(Http.Newline); if (content.contentType != null) { builder.Append(Http.ContentTypeHeaderPrefix); builder.AppendAscii(content.contentType); builder.Append(Http.Newline); } } } // // Header: Connection // if (keepAlive) { builder.Append(Http.ConnectionHeaderPrefix); builder.Append(Http.ConnectionKeepAlive); builder.Append(Http.Newline); } else { builder.Append(Http.ConnectionHeaderPrefix); builder.Append(Http.ConnectionClose); builder.Append(Http.Newline); } // // Extra Headers // if (extraHeaders != null) { builder.Append(extraHeaders); } if (extraHeadersAppender != null) { extraHeadersAppender(builder); } // // End of Headers \r\n\r\n // builder.Append(Http.Newline); // // Content // if (content.contentAsBytes != null) { builder.Append(content.contentAsBytes); } else if (content.contentAsString != null) { builder.Append(content.contentAsStringEncoder, content.contentAsString); } else if (content.contentAppender != null) { // // Get the content // var contentStart = builder.contentLength; content.contentAppender(builder); UInt32 contentLength = builder.contentLength - contentStart; // Patch the request with the new content length UInt32 shift; // Shift everything before Content-Length: value to the right if (contentLength == 0) { builder.bytes[contentLengthOffset + 9] = (Byte)'0'; shift = 9; // Shift everything } else { shift = 9; var temp = contentLength; while (true) { builder.bytes[contentLengthOffset + shift] = (Byte)('0' + (temp % 10)); temp = temp / 10; if (temp == 0) { break; } shift--; } } // Shift the beginning of the request to compensate for a smaller Content-Length if (shift > 0) { var offset = contentLengthOffset - 1; while (true) { builder.bytes[offset + shift] = builder.bytes[offset]; if (offset == 0) { break; } offset--; } } return(shift); } return(0); }