/// <summary> /// Parse the request and headers. /// </summary> /// <param name="input"></param> /// <returns></returns> private HttpRequest ParseRequest(Stream input) { // Parse the request first RequestParseState state = RequestParseState.StartLine; string line; HttpRequest request = null; try { while (m_connected && (state != RequestParseState.Body)) { // Keep trying to read a line if (!ReadLine(input, out line)) { continue; } switch (state) { case RequestParseState.StartLine: request = ParseRequestLine(line); if (request == null) { return(null); // Just let the connection close } state++; break; case RequestParseState.Headers: if (line.Length == 0) { state++; } else { ParseHeaderLine(request, line); } break; } } } catch (HttpException ex) { throw ex; } catch (Exception) { throw new HttpInternalServerErrorException("Error parsing request."); } // All done return(request); }
private async void handleRequest(StreamSocket socket) { Logger.log("XHRProxy", "Received incoming HTTP request"); DataWriter writer = new DataWriter(socket.OutputStream); writer.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8; // deny any request that isn't from localhost if (socket.Information.RemoteAddress.RawName != "::1") { Logger.log("XHRProxy", "403 Forbidden; remote address \"" + socket.Information.RemoteAddress.RawName + "\" != \"::1\""); requestError(writer, socket, "403 Forbidden", "Forbidden"); return; } DataReader reader = new DataReader(socket.InputStream); reader.InputStreamOptions = InputStreamOptions.Partial; RequestParseState state = RequestParseState.GetRequest; string urlPrefix = "/fetch/" + this.securityToken + "/"; UInt32 numBytesRead = 0; UInt32 requestBufferSize = 4096; string buffer = ""; string httpMethod = ""; string url = ""; bool isLocalFile = true; Uri httpUri = null; int p, q; bool needMoreData = true; HttpWebRequest request = null; Dictionary <string, string> headers = new Dictionary <string, string>(); int responseBufferSize = 4096; int responseBytesRead = 0; int responseTotalBytesRead = 0; byte[] responseBuffer = new byte[responseBufferSize]; try { while (true) { Logger.log("XHRProxy", "State = " + states[(int)state]); if (needMoreData) { numBytesRead = await reader.LoadAsync(requestBufferSize); Logger.log("XHRProxy", "Read " + numBytesRead + " bytes"); buffer += reader.ReadString(numBytesRead); } switch (state) { case RequestParseState.GetRequest: // go until first \n p = buffer.IndexOf("\r\n"); if (p != -1) { // parse out the request string[] tokens = buffer.Substring(0, p).Split(' '); if (tokens.Length != 3) { Logger.log("XHRProxy", "400 Bad Request; request had " + tokens.Length + " tokens, expected 3"); requestError(writer, socket, "400 Bad Request", "Bad Request"); return; } httpMethod = tokens[0].ToUpper(); Logger.log("XHRProxy", "Method = " + httpMethod); url = tokens[1]; q = url.IndexOf(urlPrefix); if (q != 0) { Logger.log("XHRProxy", "400 Bad Request"); requestError(writer, socket, "400 Bad Request", "Bad Request"); return; } string encodedUrl = url.Substring(q + urlPrefix.Length); string decodedUrl = HttpUtility.UrlDecode(encodedUrl).Replace(' ', '+'); byte[] data = Convert.FromBase64String(decodedUrl); url = Encoding.UTF8.GetString(data, 0, data.Length); if (url.IndexOf("http://") == 0 || url.IndexOf("https://") == 0) { isLocalFile = false; } Logger.log("XHRProxy", "URL = " + url); buffer = buffer.Substring(p + 2); needMoreData = false; state = RequestParseState.Headers; } else if (numBytesRead < requestBufferSize) { // not enough data, bad request Logger.log("XHRProxy", "400 Bad Request; not enough data"); requestError(writer, socket, "400 Bad Request", "Bad Request"); return; } else { // need more data Logger.log("XHRProxy", "Need more data..."); needMoreData = true; } break; case RequestParseState.Headers: p = buffer.IndexOf("\r\n\r\n"); // two line breaks if (p != -1) { Logger.log("XHRProxy", "Original HTTP Request Headers:"); string[] lines = buffer.Substring(0, p).Split('\n'); foreach (string line in lines) { q = line.IndexOf(':'); if (q != -1) { string name = line.Substring(0, q); string value = line.Substring(q + 2).Trim(); Logger.log("XHRProxy", " " + name + ": " + value); headers[name] = value; } else { Logger.log("XHRProxy", " Bad HTTP header \"" + line + "\", ignoring"); } } buffer = buffer.Substring(p + 4); state = isLocalFile ? RequestParseState.ServeFile : RequestParseState.CreateRequest; } else if (numBytesRead < requestBufferSize) { // not enough data, bad request Logger.log("XHRProxy", "400 Bad Request; not enough data"); requestError(writer, socket, "400 Bad Request", "Bad Request"); return; } else { // need more data Logger.log("XHRProxy", "Need more data..."); needMoreData = true; } if (state == RequestParseState.ServeFile) { continue; } break; case RequestParseState.ServeFile: string originalFile = url; string file = collapsePath(originalFile); if (file.IndexOf("..") == 0) { Logger.log("XHRProxy", "400 Bad Request"); Logger.log("XHRProxy", "Original file: " + originalFile); Logger.log("XHRProxy", "Resolved file: " + file); requestError(writer, socket, "400 Bad Request", "The requested file must not begin with \"..\""); return; } file = file.Replace('/', '\\'); if (file.StartsWith("\\")) { file = "App" + file; } else { file = "App\\" + file; } StorageFolder installFolder = Windows.ApplicationModel.Package.Current.InstalledLocation; StorageFile theFile; try { theFile = await installFolder.GetFileAsync(file); } catch (Exception e) { Logger.log("XHRProxy", "404 File Not Found"); Logger.log("XHRProxy", "Original file: " + originalFile); Logger.log("XHRProxy", "Resolved file: " + file); requestError(writer, socket, "404 File Not Found", "File Not Found"); return; } var randomAccessStream = await theFile.OpenReadAsync(); Stream fs = randomAccessStream.AsStreamForRead(); FileInfo fi = new FileInfo(file); string ext = fi.Extension.Substring(1); // trim the dot string mimetype = "application/octet-stream"; if (mimeTypes.ContainsKey(ext)) { mimetype = mimeTypes[ext]; } Logger.log("XHRProxy", "Status: 200 OK"); Logger.log("XHRProxy", "Actual HTTP headers being returned:"); Logger.log("XHRProxy", " Content-Type: " + mimetype); Logger.log("XHRProxy", " Content-Length: " + fi.Length); Logger.log("XHRProxy", " Connection: close"); writer.WriteString("HTTP/1.0 200 OK\r\n"); writer.WriteString("Content-Type: " + mimetype + "\r\n"); writer.WriteString("Content-Length: " + fi.Length + "\r\n"); writer.WriteString("Connection: close\r\n\r\n"); while ((responseBytesRead = fs.Read(responseBuffer, 0, responseBuffer.Length)) > 0) { responseTotalBytesRead += responseBytesRead; writer.WriteBytes(responseBuffer); } Logger.log("XHRProxy", "Returned " + responseTotalBytesRead + " bytes"); await writer.StoreAsync(); socket.Dispose(); return; case RequestParseState.CreateRequest: httpUri = new Uri(url, UriKind.Absolute); request = (HttpWebRequest)WebRequest.CreateHttp(httpUri); request.Method = httpMethod; Logger.log("XHRProxy", "Actual HTTP headers being sent:"); foreach (string key in headers.Keys) { if (key == "Accept") { Logger.log("XHRProxy", " Accept: " + headers[key]); request.Accept = headers[key]; } else if (key == "Content-Type") { if (httpMethod == "POST" || httpMethod == "PUT") { Logger.log("XHRProxy", " Content-Type: " + headers[key]); request.ContentType = headers[key]; } } else if (key == "Host") { Logger.log("XHRProxy", " Host: " + httpUri.Host); request.Headers["Host"] = httpUri.Host; } else { Logger.log("XHRProxy", " " + key + ": " + headers[key]); request.Headers[key] = headers[key]; } } if (httpMethod == "POST" || httpMethod == "PUT") { Stream requestStream = await Task.Factory.FromAsync <Stream>(request.BeginGetRequestStream, request.EndGetRequestStream, null); byte[] jsonAsBytes = Encoding.UTF8.GetBytes(buffer); Logger.log("XHRProxy", "Body:"); Logger.log("XHRProxy", buffer); await requestStream.WriteAsync(jsonAsBytes, 0, jsonAsBytes.Length); if (numBytesRead == requestBufferSize) { // pump the rest of the data while (true) { numBytesRead = await reader.LoadAsync(requestBufferSize); if (numBytesRead == 0) { break; } Logger.log("XHRProxy", "Read " + numBytesRead + " bytes"); buffer = reader.ReadString(numBytesRead); Logger.log("XHRProxy", buffer); byte[] jsonAsBytes2 = Encoding.UTF8.GetBytes(buffer); await requestStream.WriteAsync(jsonAsBytes2, 0, jsonAsBytes2.Length); if (numBytesRead < requestBufferSize) { break; } } } requestStream.Close(); } Logger.log("XHRProxy", "Sending request..."); request.BeginGetResponse(async callbackResult => { try { Logger.log("XHRProxy", "Reading response..."); HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult); Logger.log("XHRProxy", "Status: " + (int)response.StatusCode + " " + response.StatusDescription); writer.WriteString("HTTP/1.0 " + (int)response.StatusCode + " " + response.StatusDescription + "\r\n"); Logger.log("XHRProxy", "Original HTTP response headers:"); foreach (string key in response.Headers.AllKeys) { Logger.log("XHRProxy", " " + key + ": " + response.Headers[key]); } Logger.log("XHRProxy", "Actual HTTP headers being returned:"); foreach (string key in response.Headers.AllKeys) { if (key == "Connection") { Logger.log("XHRProxy", " Connection: close"); writer.WriteString("Connection: close\r\n"); } else { Logger.log("XHRProxy", " " + key + ": " + response.Headers[key]); writer.WriteString(key + ": " + response.Headers[key] + "\r\n"); } } writer.WriteString("\r\n"); Stream responseStream = response.GetResponseStream(); BinaryReader br = new BinaryReader(responseStream); byte[] responseBytes = br.ReadBytes(4096); while (responseBytes.Length > 0) { responseTotalBytesRead += responseBytes.Length; writer.WriteBytes(responseBytes); responseBytes = br.ReadBytes(4096); } Logger.log("XHRProxy", "Returned " + responseTotalBytesRead + " bytes"); await writer.StoreAsync(); socket.Dispose(); } catch (WebException ex) { // check if we have an expired or self-signed cert if (ex.Status == WebExceptionStatus.UnknownError) { if (ex.Response.Headers.Count == 0 && httpUri.Scheme == "https") { Logger.log("XHRProxy", "Invalid SSL certificate, returning a 400 Bad Request"); requestError(writer, socket, "400 Bad Request", "Invalid SSL certificate"); } else { Logger.log("XHRProxy", "File not found, returning a 404"); requestError(writer, socket, "404 File Not Found", "File Not Found"); } } else { Logger.log("XHRProxy", "400 Bad Request"); Logger.log("XHRProxy", ex.Status.ToString()); requestError(writer, socket, "400 Bad Request", ex.Status.ToString()); } return; } }, null); return; } } } catch (Exception ex) { Logger.log("XHRProxy", "500 Internal Server Error"); foreach (string s in ex.ToString().Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None)) { Logger.log("XHRProxy", s); } requestError(writer, socket, "500 Internal Server Error", ex.Message); } }