| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.ServiceModel.Channels
- {
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Net;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Net.Mime;
- using System.Net.Security;
- using System.Net.Sockets;
- using System.Reflection;
- using System.Runtime;
- using System.Runtime.CompilerServices;
- using System.Runtime.Diagnostics;
- using System.Security.Authentication.ExtendedProtection;
- using System.Security.Principal;
- using System.ServiceModel;
- using System.ServiceModel.Activation;
- using System.ServiceModel.Diagnostics;
- using System.ServiceModel.Diagnostics.Application;
- using System.ServiceModel.Security;
- using System.ServiceModel.Security.Tokens;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Xml;
- // abstract out the common functionality of an "HttpInput"
- abstract class HttpInput
- {
- const string multipartRelatedMediaType = "multipart/related";
- const string startInfoHeaderParam = "start-info";
- const string defaultContentType = "application/octet-stream";
- BufferManager bufferManager;
- bool isRequest;
- MessageEncoder messageEncoder;
- IHttpTransportFactorySettings settings;
- bool streamed;
- WebException webException;
- Stream inputStream;
- bool enableChannelBinding;
- bool errorGettingInputStream;
- protected HttpInput(IHttpTransportFactorySettings settings, bool isRequest, bool enableChannelBinding)
- {
- this.settings = settings;
- this.bufferManager = settings.BufferManager;
- this.messageEncoder = settings.MessageEncoderFactory.Encoder;
- this.webException = null;
- this.isRequest = isRequest;
- this.inputStream = null;
- this.enableChannelBinding = enableChannelBinding;
- if (isRequest)
- {
- this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode);
- }
- else
- {
- this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode);
- }
- }
- internal static HttpInput CreateHttpInput(HttpWebResponse httpWebResponse, IHttpTransportFactorySettings settings, ChannelBinding channelBinding)
- {
- return new WebResponseHttpInput(httpWebResponse, settings, channelBinding);
- }
- internal WebException WebException
- {
- get { return webException; }
- set { webException = value; }
- }
- // Note: This method will return null in the case where throwOnError is false, and a non-fatal error occurs.
- // Please exercice caution when passing in throwOnError = false. This should basically only be done in error
- // code paths, or code paths where there is very good reason that you would not want this method to throw.
- // When passing in throwOnError = false, please handle the case where this method returns null.
- public Stream GetInputStream(bool throwOnError)
- {
- if (inputStream == null && (throwOnError || !this.errorGettingInputStream))
- {
- try
- {
- inputStream = GetInputStream();
- this.errorGettingInputStream = false;
- }
- catch (Exception e)
- {
- this.errorGettingInputStream = true;
- if (throwOnError || Fx.IsFatal(e))
- {
- throw;
- }
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
- }
- }
- return inputStream;
- }
- // -1 if chunked
- public abstract long ContentLength { get; }
- protected abstract string ContentTypeCore { get; }
- protected abstract bool HasContent { get; }
- protected abstract string SoapActionHeader { get; }
- protected abstract Stream GetInputStream();
- protected virtual ChannelBinding ChannelBinding { get { return null; } }
- protected string ContentType
- {
- get
- {
- string contentType = ContentTypeCore;
- if (string.IsNullOrEmpty(contentType))
- {
- return defaultContentType;
- }
- return contentType;
- }
- }
- void ThrowMaxReceivedMessageSizeExceeded()
- {
- if (TD.MaxReceivedMessageSizeExceededIsEnabled())
- {
- TD.MaxReceivedMessageSizeExceeded(SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize));
- }
- if (isRequest)
- {
- ThrowHttpProtocolException(SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize), HttpStatusCode.RequestEntityTooLarge);
- }
- else
- {
- string message = SR.GetString(SR.MaxReceivedMessageSizeExceeded, settings.MaxReceivedMessageSize);
- Exception inner = new QuotaExceededException(message);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(message, inner));
- }
- }
- Message DecodeBufferedMessage(ArraySegment<byte> buffer, Stream inputStream)
- {
- try
- {
- // if we're chunked, make sure we've consumed the whole body
- if (ContentLength == -1 && buffer.Count == settings.MaxReceivedMessageSize)
- {
- byte[] extraBuffer = new byte[1];
- int extraReceived = inputStream.Read(extraBuffer, 0, 1);
- if (extraReceived > 0)
- {
- ThrowMaxReceivedMessageSizeExceeded();
- }
- }
- try
- {
- return messageEncoder.ReadMessage(buffer, bufferManager, ContentType);
- }
- catch (XmlException xmlException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
- }
- }
- finally
- {
- inputStream.Close();
- }
- }
- Message ReadBufferedMessage(Stream inputStream)
- {
- ArraySegment<byte> messageBuffer = GetMessageBuffer();
- byte[] buffer = messageBuffer.Array;
- int offset = 0;
- int count = messageBuffer.Count;
- while (count > 0)
- {
- int bytesRead = inputStream.Read(buffer, offset, count);
- if (bytesRead == 0) // EOF
- {
- if (ContentLength != -1)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.HttpContentLengthIncorrect)));
- }
- break;
- }
- count -= bytesRead;
- offset += bytesRead;
- }
- return DecodeBufferedMessage(new ArraySegment<byte>(buffer, 0, offset), inputStream);
- }
- Message ReadChunkedBufferedMessage(Stream inputStream)
- {
- try
- {
- return messageEncoder.ReadMessage(inputStream, bufferManager, settings.MaxBufferSize, ContentType);
- }
- catch (XmlException xmlException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
- }
- }
- Message ReadStreamedMessage(Stream inputStream)
- {
- MaxMessageSizeStream maxMessageSizeStream = new MaxMessageSizeStream(inputStream, settings.MaxReceivedMessageSize);
- try
- {
- return messageEncoder.ReadMessage(maxMessageSizeStream, settings.MaxBufferSize, ContentType);
- }
- catch (XmlException xmlException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.MessageXmlProtocolError), xmlException));
- }
- }
- protected abstract void AddProperties(Message message);
- void ApplyChannelBinding(Message message)
- {
- if (this.enableChannelBinding)
- {
- ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, message, true);
- }
- }
- // makes sure that appropriate HTTP level headers are included in the received Message
- Exception ProcessHttpAddressing(Message message)
- {
- Exception result = null;
- AddProperties(message);
- // check if user is receiving WS-1 messages
- if (message.Version.Addressing == AddressingVersion.None)
- {
- bool actionAbsent = false;
- try
- {
- actionAbsent = (message.Headers.Action == null);
- }
- catch (XmlException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- catch (CommunicationException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- if (!actionAbsent)
- {
- result = new ProtocolException(SR.GetString(SR.HttpAddressingNoneHeaderOnWire,
- XD.AddressingDictionary.Action.Value));
- }
- bool toAbsent = false;
- try
- {
- toAbsent = (message.Headers.To == null);
- }
- catch (XmlException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- catch (CommunicationException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- if (!toAbsent)
- {
- result = new ProtocolException(SR.GetString(SR.HttpAddressingNoneHeaderOnWire,
- XD.AddressingDictionary.To.Value));
- }
- message.Headers.To = message.Properties.Via;
- }
- if (isRequest)
- {
- string action = null;
- if (message.Version.Envelope == EnvelopeVersion.Soap11)
- {
- action = SoapActionHeader;
- }
- else if (message.Version.Envelope == EnvelopeVersion.Soap12 && !String.IsNullOrEmpty(ContentType))
- {
- ContentType parsedContentType = new ContentType(ContentType);
- if (parsedContentType.MediaType == multipartRelatedMediaType && parsedContentType.Parameters.ContainsKey(startInfoHeaderParam))
- {
- // fix to grab action from start-info as stated in RFC2387
- action = new ContentType(parsedContentType.Parameters[startInfoHeaderParam]).Parameters["action"];
- }
- if (action == null)
- {
- // only if we can't find an action inside start-info
- action = parsedContentType.Parameters["action"];
- }
- }
- if (action != null)
- {
- action = UrlUtility.UrlDecode(action, Encoding.UTF8);
- if (action.Length >= 2 && action[0] == '"' && action[action.Length - 1] == '"')
- {
- action = action.Substring(1, action.Length - 2);
- }
- if (message.Version.Addressing == AddressingVersion.None)
- {
- message.Headers.Action = action;
- }
- try
- {
- if (action.Length > 0 && string.Compare(message.Headers.Action, action, StringComparison.Ordinal) != 0)
- {
- result = new ActionMismatchAddressingException(SR.GetString(SR.HttpSoapActionMismatchFault,
- message.Headers.Action, action), message.Headers.Action, action);
- }
- }
- catch (XmlException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- catch (CommunicationException e)
- {
- DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
- }
- }
- }
- ApplyChannelBinding(message);
- if (DiagnosticUtility.ShouldUseActivity)
- {
- TraceUtility.TransferFromTransport(message);
- }
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.MessageReceived, SR.GetString(SR.TraceCodeMessageReceived),
- MessageTransmitTraceRecord.CreateReceiveTraceRecord(message), this, null, message);
- }
- // MessageLogger doesn't log AddressingVersion.None in the encoder since we want to make sure we log
- // as much of the message as possible. Here we log after stamping the addressing information
- if (MessageLogger.LoggingEnabled && message.Version.Addressing == AddressingVersion.None)
- {
- MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportReceive | MessageLoggingSource.LastChance);
- }
- return result;
- }
- void ValidateContentType()
- {
- if (!HasContent)
- return;
- if (string.IsNullOrEmpty(ContentType))
- {
- if (MessageLogger.ShouldLogMalformed)
- {
- // We pass in throwOnError = false below so that the exception which is eventually thrown is the ProtocolException below, with Http status code 415 "UnsupportedMediaType"
- Stream stream = this.GetInputStream(false);
- if (stream != null)
- {
- MessageLogger.LogMessage(stream, MessageLoggingSource.Malformed);
- }
- }
- ThrowHttpProtocolException(SR.GetString(SR.HttpContentTypeHeaderRequired), HttpStatusCode.UnsupportedMediaType, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing);
- }
- if (!messageEncoder.IsContentTypeSupported(ContentType))
- {
- if (MessageLogger.ShouldLogMalformed)
- {
- // We pass in throwOnError = false below so that the exception which is eventually thrown is the ProtocolException below, with Http status code 415 "UnsupportedMediaType"
- Stream stream = this.GetInputStream(false);
- if (stream != null)
- {
- MessageLogger.LogMessage(stream, MessageLoggingSource.Malformed);
- }
- }
- string statusDescription = string.Format(CultureInfo.InvariantCulture, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMismatch, ContentType, messageEncoder.ContentType);
- ThrowHttpProtocolException(SR.GetString(SR.ContentTypeMismatch, ContentType, messageEncoder.ContentType), HttpStatusCode.UnsupportedMediaType, statusDescription);
- }
- }
- public IAsyncResult BeginParseIncomingMessage(AsyncCallback callback, object state)
- {
- return this.BeginParseIncomingMessage(null, callback, state);
- }
- public IAsyncResult BeginParseIncomingMessage(HttpRequestMessage httpRequestMessage, AsyncCallback callback, object state)
- {
- bool throwing = true;
- try
- {
- IAsyncResult result = new ParseMessageAsyncResult(httpRequestMessage, this, callback, state);
- throwing = false;
- return result;
- }
- finally
- {
- if (throwing)
- {
- Close();
- }
- }
- }
- public Message EndParseIncomingMessage(IAsyncResult result, out Exception requestException)
- {
- bool throwing = true;
- try
- {
- Message message = ParseMessageAsyncResult.End(result, out requestException);
- throwing = false;
- return message;
- }
- finally
- {
- if (throwing)
- {
- Close();
- }
- }
- }
- public HttpRequestMessageHttpInput CreateHttpRequestMessageInput()
- {
- HttpRequestMessage message = new HttpRequestMessage();
- if (this.HasContent)
- {
- message.Content = new StreamContent(new MaxMessageSizeStream(this.GetInputStream(true), this.settings.MaxReceivedMessageSize));
- }
- HttpChannelUtilities.EnsureHttpRequestMessageContentNotNull(message);
- this.ConfigureHttpRequestMessage(message);
- ChannelBinding channelBinding = this.enableChannelBinding ? this.ChannelBinding : null;
- return new HttpRequestMessageHttpInput(message, this.settings, this.enableChannelBinding, channelBinding);
- }
- public abstract void ConfigureHttpRequestMessage(HttpRequestMessage message);
- public Message ParseIncomingMessage(out Exception requestException)
- {
- return this.ParseIncomingMessage(null, out requestException);
- }
- public Message ParseIncomingMessage(HttpRequestMessage httpRequestMessage, out Exception requestException)
- {
- Message message = null;
- requestException = null;
- bool throwing = true;
- try
- {
- ValidateContentType();
- ServiceModelActivity activity = null;
- if (DiagnosticUtility.ShouldUseActivity &&
- ((ServiceModelActivity.Current == null) ||
- (ServiceModelActivity.Current.ActivityType != ActivityType.ProcessAction)))
- {
- activity = ServiceModelActivity.CreateBoundedActivity(true);
- }
- using (activity)
- {
- if (DiagnosticUtility.ShouldUseActivity && activity != null)
- {
- // Only update the Start identifier if the activity is not null.
- ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityProcessingMessage, TraceUtility.RetrieveMessageNumber()), ActivityType.ProcessMessage);
- }
- if (!this.HasContent)
- {
- if (this.messageEncoder.MessageVersion == MessageVersion.None)
- {
- message = new NullMessage();
- }
- else
- {
- return null;
- }
- }
- else
- {
- Stream stream = this.GetInputStream(true);
- if (streamed)
- {
- message = ReadStreamedMessage(stream);
- }
- else if (this.ContentLength == -1)
- {
- message = ReadChunkedBufferedMessage(stream);
- }
- else
- {
- if (httpRequestMessage == null)
- {
- message = ReadBufferedMessage(stream);
- }
- else
- {
- message = ReadBufferedMessage(httpRequestMessage);
- }
- }
- }
- requestException = ProcessHttpAddressing(message);
- throwing = false;
- return message;
- }
- }
- finally
- {
- if (throwing)
- {
- Close();
- }
- }
- }
- Message ReadBufferedMessage(HttpRequestMessage httpRequestMessage)
- {
- Fx.Assert(httpRequestMessage != null, "httpRequestMessage cannot be null.");
- Message message;
- using (HttpContent currentContent = httpRequestMessage.Content)
- {
- int length = (int)this.ContentLength;
- byte[] buffer = this.bufferManager.TakeBuffer(length);
- bool success = false;
- try
- {
- MemoryStream ms = new MemoryStream(buffer);
- currentContent.CopyToAsync(ms).Wait<CommunicationException>();
- httpRequestMessage.Content = new ByteArrayContent(buffer, 0, length);
- foreach (var header in currentContent.Headers)
- {
- httpRequestMessage.Content.Headers.Add(header.Key, header.Value);
- }
- //
- message = this.messageEncoder.ReadMessage(new ArraySegment<byte>(buffer, 0, length), this.bufferManager, this.ContentType);
- success = true;
- }
- finally
- {
- if (!success)
- {
- // We don't have to return it in success case since the buffer will be returned to bufferManager when the message is disposed.
- this.bufferManager.ReturnBuffer(buffer);
- }
- }
- }
- return message;
- }
- void ThrowHttpProtocolException(string message, HttpStatusCode statusCode)
- {
- ThrowHttpProtocolException(message, statusCode, null);
- }
- void ThrowHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateHttpProtocolException(message, statusCode, statusDescription, webException));
- }
- internal static ProtocolException CreateHttpProtocolException(string message, HttpStatusCode statusCode, string statusDescription, Exception innerException)
- {
- ProtocolException exception = new ProtocolException(message, innerException);
- exception.Data.Add(HttpChannelUtilities.HttpStatusCodeExceptionKey, statusCode);
- if (statusDescription != null && statusDescription.Length > 0)
- {
- exception.Data.Add(HttpChannelUtilities.HttpStatusDescriptionExceptionKey, statusDescription);
- }
- return exception;
- }
- protected virtual void Close()
- {
- }
- ArraySegment<byte> GetMessageBuffer()
- {
- long count = ContentLength;
- int bufferSize;
- if (count > settings.MaxReceivedMessageSize)
- {
- ThrowMaxReceivedMessageSizeExceeded();
- }
- bufferSize = (int)count;
- return new ArraySegment<byte>(bufferManager.TakeBuffer(bufferSize), 0, bufferSize);
- }
- class ParseMessageAsyncResult : TraceAsyncResult
- {
- ArraySegment<byte> buffer;
- int count;
- int offset;
- HttpInput httpInput;
- Stream inputStream;
- Message message;
- Exception requestException = null;
- HttpRequestMessage httpRequestMessage;
- static AsyncCallback onRead = Fx.ThunkCallback(new AsyncCallback(OnRead));
- public ParseMessageAsyncResult(
- HttpRequestMessage httpRequestMessage,
- HttpInput httpInput,
- AsyncCallback callback,
- object state)
- : base(callback, state)
- {
- this.httpInput = httpInput;
- this.httpRequestMessage = httpRequestMessage;
- this.BeginParse();
- }
- void BeginParse()
- {
- httpInput.ValidateContentType();
- this.inputStream = httpInput.GetInputStream(true);
- if (!httpInput.HasContent)
- {
- if (httpInput.messageEncoder.MessageVersion == MessageVersion.None)
- {
- this.message = new NullMessage();
- }
- else
- {
- base.Complete(true);
- return;
- }
- }
- else if (httpInput.streamed || httpInput.ContentLength == -1)
- {
- if (httpInput.streamed)
- {
- this.message = httpInput.ReadStreamedMessage(inputStream);
- }
- else
- {
- this.message = httpInput.ReadChunkedBufferedMessage(inputStream);
- }
- }
- if (this.message != null)
- {
- this.requestException = httpInput.ProcessHttpAddressing(this.message);
- base.Complete(true);
- return;
- }
- AsyncCompletionResult result;
- if (httpRequestMessage == null)
- {
- result = this.DecodeBufferedMessageAsync();
- }
- else
- {
- result = this.DecodeBufferedHttpRequestMessageAsync();
- }
- if (result == AsyncCompletionResult.Completed)
- {
- base.Complete(true);
- }
- }
- AsyncCompletionResult DecodeBufferedMessageAsync()
- {
- this.buffer = this.httpInput.GetMessageBuffer();
- this.count = this.buffer.Count;
- this.offset = 0;
- IAsyncResult result = inputStream.BeginRead(buffer.Array, offset, count, onRead, this);
- if (result.CompletedSynchronously)
- {
- if (ContinueReading(inputStream.EndRead(result)))
- {
- return AsyncCompletionResult.Completed;
- }
- }
- return AsyncCompletionResult.Queued;
- }
- bool ContinueReading(int bytesRead)
- {
- while (true)
- {
- if (bytesRead == 0) // EOF
- {
- break;
- }
- else
- {
- offset += bytesRead;
- count -= bytesRead;
- if (count <= 0)
- {
- break;
- }
- else
- {
- IAsyncResult result = inputStream.BeginRead(buffer.Array, offset, count, onRead, this);
- if (!result.CompletedSynchronously)
- {
- return false;
- }
- bytesRead = inputStream.EndRead(result);
- }
- }
- }
- using (DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.BoundOperation(this.CallbackActivity) : null)
- {
- using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity(true) : null)
- {
- if (DiagnosticUtility.ShouldUseActivity)
- {
- ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityProcessingMessage, TraceUtility.RetrieveMessageNumber()), ActivityType.ProcessMessage);
- }
- this.message = this.httpInput.DecodeBufferedMessage(new ArraySegment<byte>(buffer.Array, 0, offset), inputStream);
- this.requestException = this.httpInput.ProcessHttpAddressing(this.message);
- }
- return true;
- }
- }
- static void OnRead(IAsyncResult result)
- {
- if (result.CompletedSynchronously)
- return;
- ParseMessageAsyncResult thisPtr = (ParseMessageAsyncResult)result.AsyncState;
- Exception completionException = null;
- bool completeSelf;
- try
- {
- completeSelf = thisPtr.ContinueReading(thisPtr.inputStream.EndRead(result));
- }
- #pragma warning suppress 56500 // [....], transferring exception to another thread
- catch (Exception e)
- {
- if (Fx.IsFatal(e))
- {
- throw;
- }
- completeSelf = true;
- completionException = e;
- }
- if (completeSelf)
- {
- thisPtr.Complete(false, completionException);
- }
- }
- public static Message End(IAsyncResult result, out Exception requestException)
- {
- ParseMessageAsyncResult thisPtr = AsyncResult.End<ParseMessageAsyncResult>(result);
- requestException = thisPtr.requestException;
- return thisPtr.message;
- }
- AsyncCompletionResult DecodeBufferedHttpRequestMessageAsync()
- {
- // Need to consider moving this to async implemenation for HttpContent reading.(CSDMAIN: 229108)
- this.message = this.httpInput.ReadBufferedMessage(this.httpRequestMessage);
- this.requestException = this.httpInput.ProcessHttpAddressing(this.message);
- return AsyncCompletionResult.Completed;
- }
- }
- class WebResponseHttpInput : HttpInput
- {
- HttpWebResponse httpWebResponse;
- byte[] preReadBuffer;
- ChannelBinding channelBinding;
- bool hasContent;
- public WebResponseHttpInput(HttpWebResponse httpWebResponse, IHttpTransportFactorySettings settings, ChannelBinding channelBinding)
- : base(settings, false, channelBinding != null)
- {
- this.channelBinding = channelBinding;
- this.httpWebResponse = httpWebResponse;
- if (this.httpWebResponse.ContentLength == -1)
- {
- this.preReadBuffer = new byte[1];
- if (this.httpWebResponse.GetResponseStream().Read(preReadBuffer, 0, 1) == 0)
- {
- this.preReadBuffer = null;
- }
- }
- this.hasContent = (this.preReadBuffer != null || this.httpWebResponse.ContentLength > 0);
- if (!this.hasContent)
- {
- // Close the response stream to avoid leaking the connection.
- this.httpWebResponse.GetResponseStream().Close();
- }
- }
- protected override ChannelBinding ChannelBinding
- {
- get
- {
- return this.channelBinding;
- }
- }
- public override long ContentLength
- {
- get
- {
- return httpWebResponse.ContentLength;
- }
- }
- protected override string ContentTypeCore
- {
- get
- {
- return httpWebResponse.ContentType;
- }
- }
- protected override bool HasContent
- {
- get { return this.hasContent; }
- }
- protected override string SoapActionHeader
- {
- get
- {
- return httpWebResponse.Headers["SOAPAction"];
- }
- }
- protected override void AddProperties(Message message)
- {
- HttpResponseMessageProperty responseProperty = new HttpResponseMessageProperty(httpWebResponse.Headers);
- responseProperty.StatusCode = httpWebResponse.StatusCode;
- responseProperty.StatusDescription = httpWebResponse.StatusDescription;
- message.Properties.Add(HttpResponseMessageProperty.Name, responseProperty);
- message.Properties.Via = message.Version.Addressing.AnonymousUri;
- }
- public override void ConfigureHttpRequestMessage(HttpRequestMessage message)
- {
- // HTTP pipeline for client side is not implemented yet
- // DCR CSDMain 216853 is tracking this
- // This API is never going to be called in current stack
- Fx.Assert(false, "HTTP pipeline for client is not implemented yet. This method should not be called.");
- throw FxTrace.Exception.AsError(new NotSupportedException());
- }
- protected override void Close()
- {
- try
- {
- httpWebResponse.Close();
- }
- catch (Exception exception)
- {
- if (Fx.IsFatal(exception))
- throw;
- DiagnosticUtility.TraceHandledException(exception, TraceEventType.Error);
- }
- }
- protected override Stream GetInputStream()
- {
- Fx.Assert(this.HasContent, "this.HasContent must be true.");
- if (this.preReadBuffer != null)
- {
- return new WebResponseInputStream(httpWebResponse, preReadBuffer);
- }
- else
- {
- return new WebResponseInputStream(httpWebResponse);
- }
- }
- class WebResponseInputStream : DetectEofStream
- {
- // in order to avoid ----ing kernel buffers, we throttle our reads. http.sys
- // deals with this fine, but System.Net doesn't do any such throttling.
- const int maxSocketRead = 64 * 1024;
- HttpWebResponse webResponse;
- bool responseClosed;
- public WebResponseInputStream(HttpWebResponse httpWebResponse)
- : base(httpWebResponse.GetResponseStream())
- {
- this.webResponse = httpWebResponse;
- }
- public WebResponseInputStream(HttpWebResponse httpWebResponse, byte[] prereadBuffer)
- : base(new PreReadStream(httpWebResponse.GetResponseStream(), prereadBuffer))
- {
- this.webResponse = httpWebResponse;
- }
- public override void Close()
- {
- base.Close();
- CloseResponse();
- }
- protected override void OnReceivedEof()
- {
- base.OnReceivedEof();
- CloseResponse();
- }
- void CloseResponse()
- {
- if (responseClosed)
- {
- return;
- }
- responseClosed = true;
- this.webResponse.Close();
- }
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
- {
- try
- {
- return BaseStream.BeginRead(buffer, offset, Math.Min(count, maxSocketRead), callback, state);
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseIOException(ioException, TimeoutHelper.FromMilliseconds(this.ReadTimeout)));
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
- }
- }
- public override int EndRead(IAsyncResult result)
- {
- try
- {
- return BaseStream.EndRead(result);
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseIOException(ioException, TimeoutHelper.FromMilliseconds(this.ReadTimeout)));
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
- }
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- try
- {
- return BaseStream.Read(buffer, offset, Math.Min(count, maxSocketRead));
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseIOException(ioException, TimeoutHelper.FromMilliseconds(this.ReadTimeout)));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
- }
- }
- public override int ReadByte()
- {
- try
- {
- return BaseStream.ReadByte();
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(objectDisposedException.Message, objectDisposedException));
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseIOException(ioException, TimeoutHelper.FromMilliseconds(this.ReadTimeout)));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateResponseWebException(webException, this.webResponse));
- }
- }
- }
- }
- }
- // abstract out the common functionality of an "HttpOutput"
- abstract class HttpOutput
- {
- const string DefaultMimeVersion = "1.0";
- HttpAbortReason abortReason;
- bool isDisposed;
- bool isRequest;
- Message message;
- IHttpTransportFactorySettings settings;
- byte[] bufferToRecycle;
- BufferManager bufferManager;
- MessageEncoder messageEncoder;
- bool streamed;
- static Action<object> onStreamSendTimeout;
- string mtomBoundary;
- Stream outputStream;
- bool supportsConcurrentIO;
- EventTraceActivity eventTraceActivity;
- bool canSendCompressedResponses;
- protected HttpOutput(IHttpTransportFactorySettings settings, Message message, bool isRequest, bool supportsConcurrentIO)
- {
- this.settings = settings;
- this.message = message;
- this.isRequest = isRequest;
- this.bufferManager = settings.BufferManager;
- this.messageEncoder = settings.MessageEncoderFactory.Encoder;
- ICompressedMessageEncoder compressedMessageEncoder = this.messageEncoder as ICompressedMessageEncoder;
- this.canSendCompressedResponses = compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled;
- if (isRequest)
- {
- this.streamed = TransferModeHelper.IsRequestStreamed(settings.TransferMode);
- }
- else
- {
- this.streamed = TransferModeHelper.IsResponseStreamed(settings.TransferMode);
- }
- this.supportsConcurrentIO = supportsConcurrentIO;
- if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
- {
- this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
- }
- }
- protected virtual bool IsChannelBindingSupportEnabled { get { return false; } }
- protected virtual ChannelBinding ChannelBinding { get { return null; } }
- protected void Abort()
- {
- Abort(HttpAbortReason.Aborted);
- }
- public virtual void Abort(HttpAbortReason reason)
- {
- if (isDisposed)
- {
- return;
- }
- this.abortReason = reason;
- TraceRequestResponseAborted(reason);
- CleanupBuffer();
- }
- private void TraceRequestResponseAborted(HttpAbortReason reason)
- {
- if (isRequest)
- {
- if (TD.HttpChannelRequestAbortedIsEnabled())
- {
- TD.HttpChannelRequestAborted(this.eventTraceActivity);
- }
- }
- else if (TD.HttpChannelResponseAbortedIsEnabled())
- {
- TD.HttpChannelResponseAborted(this.eventTraceActivity);
- }
- if (DiagnosticUtility.ShouldTraceWarning)
- {
- TraceUtility.TraceEvent(TraceEventType.Warning,
- isRequest ? TraceCode.HttpChannelRequestAborted : TraceCode.HttpChannelResponseAborted,
- isRequest ? SR.GetString(SR.TraceCodeHttpChannelRequestAborted) : SR.GetString(SR.TraceCodeHttpChannelResponseAborted),
- this.message);
- }
- }
- public void Close()
- {
- if (isDisposed)
- {
- return;
- }
- try
- {
- if (this.outputStream != null)
- {
- outputStream.Close();
- }
- }
- finally
- {
- CleanupBuffer();
- }
- }
- void CleanupBuffer()
- {
- byte[] bufferToRecycleSnapshot = Interlocked.Exchange<byte[]>(ref this.bufferToRecycle, null);
- if (bufferToRecycleSnapshot != null)
- {
- bufferManager.ReturnBuffer(bufferToRecycleSnapshot);
- }
- isDisposed = true;
- }
- protected abstract void AddMimeVersion(string version);
- protected abstract void AddHeader(string name, string value);
- protected abstract void SetContentType(string contentType);
- protected abstract void SetContentEncoding(string contentEncoding);
- protected abstract void SetStatusCode(HttpStatusCode statusCode);
- protected abstract void SetStatusDescription(string statusDescription);
- protected virtual bool CleanupChannelBinding { get { return true; } }
- protected virtual void SetContentLength(int contentLength)
- {
- }
- protected virtual string HttpMethod { get { return null; } }
- public virtual ChannelBinding TakeChannelBinding()
- {
- return null;
- }
- private void ApplyChannelBinding()
- {
- if (this.IsChannelBindingSupportEnabled)
- {
- ChannelBindingUtility.TryAddToMessage(this.ChannelBinding, this.message, this.CleanupChannelBinding);
- }
- }
- protected abstract Stream GetOutputStream();
- protected virtual bool WillGetOutputStreamCompleteSynchronously
- {
- get { return true; }
- }
- protected bool CanSendCompressedResponses
- {
- get { return this.canSendCompressedResponses; }
- }
- protected virtual IAsyncResult BeginGetOutputStream(AsyncCallback callback, object state)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
- }
- protected virtual Stream EndGetOutputStream(IAsyncResult result)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException());
- }
- public void ConfigureHttpResponseMessage(Message message, HttpResponseMessage httpResponseMessage, HttpResponseMessageProperty responseProperty)
- {
- HttpChannelUtilities.EnsureHttpResponseMessageContentNotNull(httpResponseMessage);
- string action = message.Headers.Action;
- if (message.Version.Addressing == AddressingVersion.None)
- {
- if (MessageLogger.LogMessagesAtTransportLevel)
- {
- message.Properties.Add(AddressingProperty.Name, new AddressingProperty(message.Headers));
- }
- message.Headers.Action = null;
- message.Headers.To = null;
- }
- bool httpResponseMessagePropertyFound = responseProperty != null;
- string contentType = null;
- if (message.Version == MessageVersion.None && httpResponseMessagePropertyFound && !string.IsNullOrEmpty(responseProperty.Headers[HttpResponseHeader.ContentType]))
- {
- contentType = responseProperty.Headers[HttpResponseHeader.ContentType];
- responseProperty.Headers.Remove(HttpResponseHeader.ContentType);
- if (!messageEncoder.IsContentTypeSupported(contentType))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.ResponseContentTypeNotSupported,
- contentType)));
- }
- }
- if (string.IsNullOrEmpty(contentType))
- {
- MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
- if (mtomMessageEncoder == null)
- {
- contentType = messageEncoder.ContentType;
- }
- else
- {
- contentType = mtomMessageEncoder.GetContentType(out this.mtomBoundary);
- // For MTOM messages, add a MIME version header
- httpResponseMessage.Headers.Add(HttpChannelUtilities.MIMEVersionHeader, DefaultMimeVersion);
- }
- }
- if (isRequest && FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
- {
- EnsureEventTraceActivity(message);
- }
- if (this.CanSendCompressedResponses)
- {
- string contentEncoding;
- string compressionContentType = contentType;
- if (HttpChannelUtilities.GetHttpResponseTypeAndEncodingForCompression(ref compressionContentType, out contentEncoding))
- {
- contentType = compressionContentType;
- this.SetContentEncoding(contentEncoding);
- }
- }
- if (httpResponseMessage.Content != null && !string.IsNullOrEmpty(contentType))
- {
- MediaTypeHeaderValue mediaTypeHeaderValue;
- if (!MediaTypeHeaderValue.TryParse(contentType, out mediaTypeHeaderValue))
- {
- throw FxTrace.Exception.Argument("contentType", SR.GetString(SR.InvalidContentTypeError, contentType));
- }
- httpResponseMessage.Content.Headers.ContentType = mediaTypeHeaderValue;
- }
- bool httpMethodIsHead = string.Compare(this.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) == 0;
- if (httpMethodIsHead ||
- httpResponseMessagePropertyFound && responseProperty.SuppressEntityBody)
- {
- httpResponseMessage.Content.Headers.ContentLength = 0;
- httpResponseMessage.Content.Headers.ContentType = null;
- }
- if (httpResponseMessagePropertyFound)
- {
- httpResponseMessage.StatusCode = responseProperty.StatusCode;
- if (responseProperty.StatusDescription != null)
- {
- responseProperty.StatusDescription = responseProperty.StatusDescription;
- }
- foreach (string key in responseProperty.Headers.AllKeys)
- {
- httpResponseMessage.AddHeader(key, responseProperty.Headers[key]);
- }
- }
- if (!message.IsEmpty)
- {
- using (HttpContent content = httpResponseMessage.Content)
- {
- if (this.streamed)
- {
- IStreamedMessageEncoder streamedMessageEncoder = this.messageEncoder as IStreamedMessageEncoder;
- Stream stream = null;
- if (streamedMessageEncoder != null)
- {
- stream = streamedMessageEncoder.GetResponseMessageStream(message);
- }
- if (stream != null)
- {
- httpResponseMessage.Content = new StreamContent(stream);
- }
- else
- {
- httpResponseMessage.Content = new OpaqueContent(this.messageEncoder, message, this.mtomBoundary);
- }
- }
- else
- {
- // HttpOutputByteArrayContent assumes responsibility for returning the buffer to the bufferManager.
- ArraySegment<byte> messageBytes = this.SerializeBufferedMessage(message, false);
- httpResponseMessage.Content = new HttpOutputByteArrayContent(messageBytes.Array, messageBytes.Offset, messageBytes.Count, this.bufferManager);
- }
- httpResponseMessage.Content.Headers.Clear();
- foreach (var header in content.Headers)
- {
- httpResponseMessage.Content.Headers.Add(header.Key, header.Value);
- }
- }
- }
- }
- protected virtual bool PrepareHttpSend(Message message)
- {
- string action = message.Headers.Action;
- if (message.Version.Addressing == AddressingVersion.None)
- {
- if (MessageLogger.LogMessagesAtTransportLevel)
- {
- message.Properties.Add(AddressingProperty.Name, new AddressingProperty(message.Headers));
- }
- message.Headers.Action = null;
- message.Headers.To = null;
- }
- string contentType = null;
- if (message.Version == MessageVersion.None)
- {
- object property = null;
- if (message.Properties.TryGetValue(HttpResponseMessageProperty.Name, out property))
- {
- HttpResponseMessageProperty responseProperty = (HttpResponseMessageProperty)property;
- if (!string.IsNullOrEmpty(responseProperty.Headers[HttpResponseHeader.ContentType]))
- {
- contentType = responseProperty.Headers[HttpResponseHeader.ContentType];
- if (!messageEncoder.IsContentTypeSupported(contentType))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.ResponseContentTypeNotSupported,
- contentType)));
- }
- }
- }
- }
- if (string.IsNullOrEmpty(contentType))
- {
- MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
- if (mtomMessageEncoder == null)
- {
- contentType = messageEncoder.ContentType;
- }
- else
- {
- contentType = mtomMessageEncoder.GetContentType(out this.mtomBoundary);
- // For MTOM messages, add a MIME version header
- AddMimeVersion("1.0");
- }
- }
- if (isRequest && FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
- {
- EnsureEventTraceActivity(message);
- }
- SetContentType(contentType);
- return message is NullMessage;
- }
- protected bool PrepareHttpSend(HttpResponseMessage httpResponseMessage)
- {
- this.PrepareHttpSendCore(httpResponseMessage);
- return HttpChannelUtilities.IsEmpty(httpResponseMessage);
- }
- protected abstract void PrepareHttpSendCore(HttpResponseMessage message);
- private static void EnsureEventTraceActivity(Message message)
- {
- //We need to send this only if there is no message id.
- if (message.Headers.MessageId == null)
- {
- EventTraceActivity eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message);
- if (eventTraceActivity == null)
- {
- //Whoops no activity on the message yet.
- eventTraceActivity = new EventTraceActivity();
- EventTraceActivityHelper.TryAttachActivity(message, eventTraceActivity);
- }
- HttpRequestMessageProperty httpProperties;
- if (!message.Properties.TryGetValue<HttpRequestMessageProperty>(HttpRequestMessageProperty.Name, out httpProperties))
- {
- httpProperties = new HttpRequestMessageProperty();
- message.Properties.Add(HttpRequestMessageProperty.Name, httpProperties);
- }
- httpProperties.Headers.Add(EventTraceActivity.Name, Convert.ToBase64String(eventTraceActivity.ActivityId.ToByteArray()));
- }
- }
- ArraySegment<byte> SerializeBufferedMessage(Message message)
- {
- // by default, the HttpOutput should own the buffer and clean it up
- return SerializeBufferedMessage(message, true);
- }
- ArraySegment<byte> SerializeBufferedMessage(Message message, bool shouldRecycleBuffer)
- {
- ArraySegment<byte> result;
- MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
- if (mtomMessageEncoder == null)
- {
- result = messageEncoder.WriteMessage(message, int.MaxValue, bufferManager);
- }
- else
- {
- result = mtomMessageEncoder.WriteMessage(message, int.MaxValue, bufferManager, 0, this.mtomBoundary);
- }
- if (shouldRecycleBuffer)
- {
- // Only set this.bufferToRecycle if the HttpOutput owns the buffer, we will clean it up upon httpOutput.Close()
- // Otherwise, caller of SerializeBufferedMessage assumes responsiblity for returning the buffer to the buffer pool
- this.bufferToRecycle = result.Array;
- }
- return result;
- }
- Stream GetWrappedOutputStream()
- {
- const int ChunkSize = 32768; // buffer size used for synchronous writes
- const int BufferSize = 16384; // buffer size used for asynchronous writes
- const int BufferCount = 4; // buffer count used for asynchronous writes
- // Writing an HTTP request chunk has a high fixed cost, so use BufferedStream to avoid writing
- // small ones.
- return this.supportsConcurrentIO ? (Stream)new BufferedOutputAsyncStream(this.outputStream, BufferSize, BufferCount) : new BufferedStream(this.outputStream, ChunkSize);
- }
- void WriteStreamedMessage(TimeSpan timeout)
- {
- this.outputStream = GetWrappedOutputStream();
- // Since HTTP streams don't support timeouts, we can't just use TimeoutStream here.
- // Rather, we need to run a timer to bound the overall operation
- if (onStreamSendTimeout == null)
- {
- onStreamSendTimeout = new Action<object>(OnStreamSendTimeout);
- }
- IOThreadTimer sendTimer = new IOThreadTimer(onStreamSendTimeout, this, true);
- sendTimer.Set(timeout);
- try
- {
- MtomMessageEncoder mtomMessageEncoder = messageEncoder as MtomMessageEncoder;
- if (mtomMessageEncoder == null)
- {
- messageEncoder.WriteMessage(this.message, this.outputStream);
- }
- else
- {
- mtomMessageEncoder.WriteMessage(this.message, this.outputStream, this.mtomBoundary);
- }
- }
- finally
- {
- sendTimer.Cancel();
- }
- }
- static void OnStreamSendTimeout(object state)
- {
- HttpOutput thisPtr = (HttpOutput)state;
- thisPtr.Abort(HttpAbortReason.TimedOut);
- }
- IAsyncResult BeginWriteStreamedMessage(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
- {
- return new WriteStreamedMessageAsyncResult(timeout, this, httpResponseMessage, callback, state);
- }
- void EndWriteStreamedMessage(IAsyncResult result)
- {
- WriteStreamedMessageAsyncResult.End(result);
- }
- class HttpOutputByteArrayContent : ByteArrayContent
- {
- BufferManager bufferManager;
- volatile bool cleaned = false;
- ArraySegment<byte> content;
- public HttpOutputByteArrayContent(byte[] content, int offset, int count, BufferManager bufferManager)
- : base(content, offset, count)
- {
- Fx.Assert(bufferManager != null, "bufferManager should not be null");
- Fx.Assert(content != null, "content should not be null");
- this.content = new ArraySegment<byte>(content, offset, count);
- this.bufferManager = bufferManager;
- }
- public ArraySegment<byte> Content
- {
- get
- {
- return this.content;
- }
- }
- protected override Task<Stream> CreateContentReadStreamAsync()
- {
- return base.CreateContentReadStreamAsync().ContinueWith<Stream>(t =>
- new HttpOutputByteArrayContentStream(t.Result, this));
- }
- protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
- {
- return base.SerializeToStreamAsync(stream, context).ContinueWith(t =>
- {
- this.Cleanup();
- HttpChannelUtilities.HandleContinueWithTask(t);
- });
- }
- void Cleanup()
- {
- if (!cleaned)
- {
- lock (this)
- {
- if (!cleaned)
- {
- cleaned = true;
- this.bufferManager.ReturnBuffer(this.content.Array);
- }
- }
- }
- }
- class HttpOutputByteArrayContentStream : DelegatingStream
- {
- HttpOutputByteArrayContent content;
- public HttpOutputByteArrayContentStream(Stream innerStream, HttpOutputByteArrayContent content)
- : base(innerStream)
- {
- this.content = content;
- }
- public override void Close()
- {
- base.Close();
- this.content.Cleanup();
- }
- }
- }
- class WriteStreamedMessageAsyncResult : AsyncResult
- {
- HttpOutput httpOutput;
- IOThreadTimer sendTimer;
- static AsyncCallback onWriteStreamedMessage = Fx.ThunkCallback(OnWriteStreamedMessage);
- HttpResponseMessage httpResponseMessage;
- public WriteStreamedMessageAsyncResult(TimeSpan timeout, HttpOutput httpOutput, HttpResponseMessage httpResponseMessage, AsyncCallback callback, object state)
- : base(callback, state)
- {
- this.httpResponseMessage = httpResponseMessage;
- this.httpOutput = httpOutput;
- httpOutput.outputStream = httpOutput.GetWrappedOutputStream();
- // Since HTTP streams don't support timeouts, we can't just use TimeoutStream here.
- // Rather, we need to run a timer to bound the overall operation
- if (onStreamSendTimeout == null)
- {
- onStreamSendTimeout = new Action<object>(OnStreamSendTimeout);
- }
- this.SetTimer(timeout);
- bool completeSelf = false;
- bool throwing = true;
- try
- {
- completeSelf = HandleWriteStreamedMessage(null);
- throwing = false;
- }
- finally
- {
- if (completeSelf || throwing)
- {
- this.sendTimer.Cancel();
- }
- }
- if (completeSelf)
- {
- this.Complete(true);
- }
- }
- bool HandleWriteStreamedMessage(IAsyncResult result)
- {
- if (this.httpResponseMessage == null)
- {
- if (result == null)
- {
- MtomMessageEncoder mtomMessageEncoder = httpOutput.messageEncoder as MtomMessageEncoder;
- if (mtomMessageEncoder == null)
- {
- result = httpOutput.messageEncoder.BeginWriteMessage(httpOutput.message, httpOutput.outputStream, onWriteStreamedMessage, this);
- }
- else
- {
- result = mtomMessageEncoder.BeginWriteMessage(httpOutput.message, httpOutput.outputStream, httpOutput.mtomBoundary, onWriteStreamedMessage, this);
- }
- if (!result.CompletedSynchronously)
- {
- return false;
- }
- }
- httpOutput.messageEncoder.EndWriteMessage(result);
- return true;
- }
- else
- {
- OpaqueContent content = this.httpResponseMessage.Content as OpaqueContent;
- if (result == null)
- {
- Fx.Assert(this.httpResponseMessage.Content != null, "httpOutput.httpResponseMessage.Content should not be null.");
- if (content != null)
- {
- result = content.BeginWriteToStream(httpOutput.outputStream, onWriteStreamedMessage, this);
- }
- else
- {
- result = this.httpResponseMessage.Content.CopyToAsync(httpOutput.outputStream).AsAsyncResult(onWriteStreamedMessage, this);
- }
- if (!result.CompletedSynchronously)
- {
- return false;
- }
- }
- if (content != null)
- {
- content.EndWriteToStream(result);
- }
- return true;
- }
- }
- static void OnWriteStreamedMessage(IAsyncResult result)
- {
- if (result.CompletedSynchronously)
- {
- return;
- }
- WriteStreamedMessageAsyncResult thisPtr = (WriteStreamedMessageAsyncResult)result.AsyncState;
- Exception completionException = null;
- bool completeSelf = false;
- try
- {
- completeSelf = thisPtr.HandleWriteStreamedMessage(result);
- }
- catch (Exception ex)
- {
- if (Fx.IsFatal(ex))
- {
- throw;
- }
- completeSelf = true;
- completionException = ex;
- }
- if (completeSelf)
- {
- thisPtr.sendTimer.Cancel();
- thisPtr.Complete(false, completionException);
- }
- }
- void SetTimer(TimeSpan timeout)
- {
- Fx.Assert(this.sendTimer == null, "SetTimer should only be called once");
- this.sendTimer = new IOThreadTimer(onStreamSendTimeout, this.httpOutput, true);
- this.sendTimer.Set(timeout);
- }
- public static void End(IAsyncResult result)
- {
- AsyncResult.End<WriteStreamedMessageAsyncResult>(result);
- }
- }
- public IAsyncResult BeginSend(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
- {
- Fx.Assert(httpResponseMessage != null, "httpResponseMessage should not be null.");
- return this.BeginSendCore(httpResponseMessage, timeout, callback, state);
- }
- public IAsyncResult BeginSend(TimeSpan timeout, AsyncCallback callback, object state)
- {
- return this.BeginSendCore(null, timeout, callback, state);
- }
- IAsyncResult BeginSendCore(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, object state)
- {
- bool throwing = true;
- try
- {
- bool suppressEntityBody;
- if (httpResponseMessage != null)
- {
- suppressEntityBody = this.PrepareHttpSend(httpResponseMessage);
- }
- else
- {
- suppressEntityBody = PrepareHttpSend(message);
- }
- this.TraceHttpSendStart();
- IAsyncResult result = new SendAsyncResult(this, httpResponseMessage, suppressEntityBody, timeout, callback, state);
- throwing = false;
- return result;
- }
- finally
- {
- if (throwing)
- {
- Abort();
- }
- }
- }
- private void TraceHttpSendStart()
- {
- if (TD.HttpSendMessageStartIsEnabled())
- {
- if (streamed)
- {
- TD.HttpSendStreamedMessageStart(this.eventTraceActivity);
- }
- else
- {
- TD.HttpSendMessageStart(this.eventTraceActivity);
- }
- }
- }
- public virtual void EndSend(IAsyncResult result)
- {
- bool throwing = true;
- try
- {
- SendAsyncResult.End(result);
- throwing = false;
- }
- finally
- {
- if (throwing)
- {
- Abort();
- }
- }
- }
- void LogMessage()
- {
- if (MessageLogger.LogMessagesAtTransportLevel)
- {
- MessageLogger.LogMessage(ref message, MessageLoggingSource.TransportSend);
- }
- }
- public void Send(HttpResponseMessage httpResponseMessage, TimeSpan timeout)
- {
- bool suppressEntityBody = this.PrepareHttpSend(httpResponseMessage);
- TraceHttpSendStart();
- if (suppressEntityBody)
- {
- // requests can't always support an output stream (for GET, etc)
- if (!isRequest)
- {
- outputStream = GetOutputStream();
- }
- else
- {
- this.SetContentLength(0);
- LogMessage();
- }
- }
- else if (streamed)
- {
- outputStream = this.GetOutputStream();
- ApplyChannelBinding();
- OpaqueContent content = httpResponseMessage.Content as OpaqueContent;
- if (content != null)
- {
- content.WriteToStream(this.outputStream);
- }
- else
- {
- if (!httpResponseMessage.Content.CopyToAsync(this.outputStream).Wait<CommunicationException>(timeout))
- {
- throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.TimeoutOnSend, timeout)));
- }
- }
- }
- else
- {
- if (this.IsChannelBindingSupportEnabled)
- {
- //need to get the Channel binding token (CBT), apply channel binding info to the message and then write the message
- //CBT is only enabled when message security is in the stack, which also requires an HTTP entity body, so we
- //should be safe to always get the stream.
- outputStream = this.GetOutputStream();
- ApplyChannelBinding();
- ArraySegment<byte> buffer = SerializeBufferedMessage(httpResponseMessage);
- Fx.Assert(buffer.Count != 0, "We should always have an entity body in this case...");
- outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
- }
- else
- {
- ArraySegment<byte> buffer = SerializeBufferedMessage(httpResponseMessage);
- SetContentLength(buffer.Count);
- // requests can't always support an output stream (for GET, etc)
- if (!isRequest || buffer.Count > 0)
- {
- outputStream = this.GetOutputStream();
- outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
- }
- }
- }
- TraceSend();
- }
- ArraySegment<byte> SerializeBufferedMessage(HttpResponseMessage httpResponseMessage)
- {
- HttpOutputByteArrayContent content = httpResponseMessage.Content as HttpOutputByteArrayContent;
- if (content == null)
- {
- byte[] byteArray = httpResponseMessage.Content.ReadAsByteArrayAsync().Result;
- return new ArraySegment<byte>(byteArray, 0, byteArray.Length);
- }
- else
- {
- return content.Content;
- }
- }
- public void Send(TimeSpan timeout)
- {
- bool suppressEntityBody = PrepareHttpSend(message);
- TraceHttpSendStart();
- if (suppressEntityBody)
- {
- // requests can't always support an output stream (for GET, etc)
- if (!isRequest)
- {
- outputStream = GetOutputStream();
- }
- else
- {
- this.SetContentLength(0);
- LogMessage();
- }
- }
- else if (streamed)
- {
- outputStream = GetOutputStream();
- ApplyChannelBinding();
- WriteStreamedMessage(timeout);
- }
- else
- {
- if (this.IsChannelBindingSupportEnabled)
- {
- //need to get the Channel binding token (CBT), apply channel binding info to the message and then write the message
- //CBT is only enabled when message security is in the stack, which also requires an HTTP entity body, so we
- //should be safe to always get the stream.
- outputStream = GetOutputStream();
- ApplyChannelBinding();
- ArraySegment<byte> buffer = SerializeBufferedMessage(message);
- Fx.Assert(buffer.Count != 0, "We should always have an entity body in this case...");
- outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
- }
- else
- {
- ArraySegment<byte> buffer = SerializeBufferedMessage(message);
- SetContentLength(buffer.Count);
- // requests can't always support an output stream (for GET, etc)
- if (!isRequest || buffer.Count > 0)
- {
- outputStream = GetOutputStream();
- outputStream.Write(buffer.Array, buffer.Offset, buffer.Count);
- }
- }
- }
- TraceSend();
- }
- void TraceSend()
- {
- if (DiagnosticUtility.ShouldTraceInformation)
- {
- TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.MessageSent, SR.GetString(SR.TraceCodeMessageSent),
- new MessageTraceRecord(this.message), this, null);
- }
- if (TD.HttpSendStopIsEnabled())
- {
- TD.HttpSendStop(this.eventTraceActivity);
- }
- }
- class SendAsyncResult : AsyncResult
- {
- HttpOutput httpOutput;
- static AsyncCallback onGetOutputStream;
- static Action<object> onWriteStreamedMessageLater;
- static AsyncCallback onWriteStreamedMessage;
- static AsyncCallback onWriteBody;
- bool suppressEntityBody;
- ArraySegment<byte> buffer;
- TimeoutHelper timeoutHelper;
- HttpResponseMessage httpResponseMessage;
- public SendAsyncResult(HttpOutput httpOutput, HttpResponseMessage httpResponseMessage, bool suppressEntityBody, TimeSpan timeout, AsyncCallback callback, object state)
- : base(callback, state)
- {
- this.httpOutput = httpOutput;
- this.httpResponseMessage = httpResponseMessage;
- this.suppressEntityBody = suppressEntityBody;
- if (suppressEntityBody)
- {
- if (httpOutput.isRequest)
- {
- httpOutput.SetContentLength(0);
- this.httpOutput.TraceSend();
- this.httpOutput.LogMessage();
- base.Complete(true);
- return;
- }
- }
- this.timeoutHelper = new TimeoutHelper(timeout);
- Send();
- }
- void Send()
- {
- if (httpOutput.IsChannelBindingSupportEnabled)
- {
- SendWithChannelBindingToken();
- }
- else
- {
- SendWithoutChannelBindingToken();
- }
- }
- void SendWithoutChannelBindingToken()
- {
- if (!suppressEntityBody && !httpOutput.streamed)
- {
- if (this.httpResponseMessage != null)
- {
- buffer = httpOutput.SerializeBufferedMessage(this.httpResponseMessage);
- }
- else
- {
- buffer = httpOutput.SerializeBufferedMessage(httpOutput.message);
- }
- httpOutput.SetContentLength(buffer.Count);
- }
- if (this.httpOutput.WillGetOutputStreamCompleteSynchronously)
- {
- httpOutput.outputStream = httpOutput.GetOutputStream();
- }
- else
- {
- if (onGetOutputStream == null)
- {
- onGetOutputStream = Fx.ThunkCallback(new AsyncCallback(OnGetOutputStream));
- }
- IAsyncResult result = httpOutput.BeginGetOutputStream(onGetOutputStream, this);
- if (!result.CompletedSynchronously)
- return;
- httpOutput.outputStream = httpOutput.EndGetOutputStream(result);
- }
- if (WriteMessage(true))
- {
- this.httpOutput.TraceSend();
- base.Complete(true);
- }
- }
- void SendWithChannelBindingToken()
- {
- if (this.httpOutput.WillGetOutputStreamCompleteSynchronously)
- {
- httpOutput.outputStream = httpOutput.GetOutputStream();
- httpOutput.ApplyChannelBinding();
- }
- else
- {
- if (onGetOutputStream == null)
- {
- onGetOutputStream = Fx.ThunkCallback(new AsyncCallback(OnGetOutputStream));
- }
- IAsyncResult result = httpOutput.BeginGetOutputStream(onGetOutputStream, this);
- if (!result.CompletedSynchronously)
- return;
- httpOutput.outputStream = httpOutput.EndGetOutputStream(result);
- httpOutput.ApplyChannelBinding();
- }
- if (!httpOutput.streamed)
- {
- if (this.httpResponseMessage != null)
- {
- buffer = httpOutput.SerializeBufferedMessage(this.httpResponseMessage);
- }
- else
- {
- buffer = httpOutput.SerializeBufferedMessage(httpOutput.message);
- }
- httpOutput.SetContentLength(buffer.Count);
- }
- if (WriteMessage(true))
- {
- this.httpOutput.TraceSend();
- base.Complete(true);
- }
- }
- bool WriteMessage(bool isStillSynchronous)
- {
- if (suppressEntityBody)
- {
- return true;
- }
- if (httpOutput.streamed)
- {
- if (isStillSynchronous)
- {
- if (onWriteStreamedMessageLater == null)
- {
- onWriteStreamedMessageLater = new Action<object>(OnWriteStreamedMessageLater);
- }
- ActionItem.Schedule(onWriteStreamedMessageLater, this);
- return false;
- }
- else
- {
- return WriteStreamedMessage();
- }
- }
- else
- {
- if (onWriteBody == null)
- {
- onWriteBody = Fx.ThunkCallback(new AsyncCallback(OnWriteBody));
- }
- IAsyncResult writeResult =
- httpOutput.outputStream.BeginWrite(buffer.Array, buffer.Offset, buffer.Count, onWriteBody, this);
- if (!writeResult.CompletedSynchronously)
- {
- return false;
- }
- CompleteWriteBody(writeResult);
- }
- return true;
- }
- bool WriteStreamedMessage()
- {
- // return a bool to determine if we are [....].
- if (onWriteStreamedMessage == null)
- {
- onWriteStreamedMessage = Fx.ThunkCallback(OnWriteStreamedMessage);
- }
- return HandleWriteStreamedMessage(null); // completed synchronously
- }
- bool HandleWriteStreamedMessage(IAsyncResult result)
- {
- if (result == null)
- {
- result = httpOutput.BeginWriteStreamedMessage(this.httpResponseMessage, timeoutHelper.RemainingTime(), onWriteStreamedMessage, this);
- if (!result.CompletedSynchronously)
- {
- return false;
- }
- }
- httpOutput.EndWriteStreamedMessage(result);
- return true;
- }
- static void OnWriteStreamedMessage(IAsyncResult result)
- {
- if (result.CompletedSynchronously)
- {
- return;
- }
- SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
- Exception completionException = null;
- bool completeSelf = false;
- try
- {
- completeSelf = thisPtr.HandleWriteStreamedMessage(result);
- }
- catch (Exception ex)
- {
- if (Fx.IsFatal(ex))
- {
- throw;
- }
- completeSelf = true;
- completionException = ex;
- }
- if (completeSelf)
- {
- if (completionException != null)
- {
- thisPtr.httpOutput.TraceSend();
- }
- thisPtr.Complete(false, completionException);
- }
- }
- void CompleteWriteBody(IAsyncResult result)
- {
- httpOutput.outputStream.EndWrite(result);
- }
- public static void End(IAsyncResult result)
- {
- AsyncResult.End<SendAsyncResult>(result);
- }
- static void OnGetOutputStream(IAsyncResult result)
- {
- if (result.CompletedSynchronously)
- return;
- SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
- Exception completionException = null;
- bool completeSelf = false;
- try
- {
- thisPtr.httpOutput.outputStream = thisPtr.httpOutput.EndGetOutputStream(result);
- thisPtr.httpOutput.ApplyChannelBinding();
- if (!thisPtr.httpOutput.streamed && thisPtr.httpOutput.IsChannelBindingSupportEnabled)
- {
- thisPtr.buffer = thisPtr.httpOutput.SerializeBufferedMessage(thisPtr.httpOutput.message);
- thisPtr.httpOutput.SetContentLength(thisPtr.buffer.Count);
- }
- if (thisPtr.WriteMessage(false))
- {
- thisPtr.httpOutput.TraceSend();
- completeSelf = true;
- }
- }
- #pragma warning suppress 56500 // [....], transferring exception to another thread
- catch (Exception e)
- {
- if (Fx.IsFatal(e))
- {
- throw;
- }
- completeSelf = true;
- completionException = e;
- }
- if (completeSelf)
- {
- thisPtr.Complete(false, completionException);
- }
- }
- static void OnWriteStreamedMessageLater(object state)
- {
- SendAsyncResult thisPtr = (SendAsyncResult)state;
- bool completeSelf = false;
- Exception completionException = null;
- try
- {
- completeSelf = thisPtr.WriteStreamedMessage();
- }
- #pragma warning suppress 56500 // [....], transferring exception to another thread
- catch (Exception e)
- {
- if (Fx.IsFatal(e))
- {
- throw;
- }
- completeSelf = true;
- completionException = e;
- }
- if (completeSelf)
- {
- if (completionException != null)
- {
- thisPtr.httpOutput.TraceSend();
- }
- thisPtr.Complete(false, completionException);
- }
- }
- static void OnWriteBody(IAsyncResult result)
- {
- if (result.CompletedSynchronously)
- return;
- SendAsyncResult thisPtr = (SendAsyncResult)result.AsyncState;
- Exception completionException = null;
- try
- {
- thisPtr.CompleteWriteBody(result);
- thisPtr.httpOutput.TraceSend();
- }
- #pragma warning suppress 56500 // [....], transferring exception to another thread
- catch (Exception e)
- {
- if (Fx.IsFatal(e))
- {
- throw;
- }
- completionException = e;
- }
- thisPtr.Complete(false, completionException);
- }
- }
- internal static HttpOutput CreateHttpOutput(HttpWebRequest httpWebRequest, IHttpTransportFactorySettings settings, Message message, bool enableChannelBindingSupport)
- {
- return new WebRequestHttpOutput(httpWebRequest, settings, message, enableChannelBindingSupport);
- }
- internal static HttpOutput CreateHttpOutput(HttpListenerResponse httpListenerResponse, IHttpTransportFactorySettings settings, Message message, string httpMethod)
- {
- return new ListenerResponseHttpOutput(httpListenerResponse, settings, message, httpMethod);
- }
- class WebRequestHttpOutput : HttpOutput
- {
- HttpWebRequest httpWebRequest;
- ChannelBinding channelBindingToken;
- bool enableChannelBindingSupport;
- public WebRequestHttpOutput(HttpWebRequest httpWebRequest, IHttpTransportFactorySettings settings, Message message, bool enableChannelBindingSupport)
- : base(settings, message, true, false)
- {
- this.httpWebRequest = httpWebRequest;
- this.enableChannelBindingSupport = enableChannelBindingSupport;
- }
- public override void Abort(HttpAbortReason abortReason)
- {
- httpWebRequest.Abort();
- base.Abort(abortReason);
- }
- protected override void AddMimeVersion(string version)
- {
- httpWebRequest.Headers[HttpChannelUtilities.MIMEVersionHeader] = version;
- }
- protected override void AddHeader(string name, string value)
- {
- httpWebRequest.Headers.Add(name, value);
- }
- protected override void SetContentType(string contentType)
- {
- httpWebRequest.ContentType = contentType;
- }
- protected override void SetContentEncoding(string contentEncoding)
- {
- this.httpWebRequest.Headers.Add(HttpChannelUtilities.ContentEncodingHeader, contentEncoding);
- }
- protected override void SetContentLength(int contentLength)
- {
- if (contentLength == 0 // work around whidbey issue with setting ContentLength - (see MB36881)
- && !this.enableChannelBindingSupport) //When ChannelBinding is enabled, content length isn't supported
- {
- httpWebRequest.ContentLength = contentLength;
- }
- }
- protected override void SetStatusCode(HttpStatusCode statusCode)
- {
- }
- protected override void SetStatusDescription(string statusDescription)
- {
- }
- protected override bool WillGetOutputStreamCompleteSynchronously
- {
- get { return false; }
- }
- protected override bool IsChannelBindingSupportEnabled
- {
- get
- {
- return this.enableChannelBindingSupport;
- }
- }
- protected override ChannelBinding ChannelBinding
- {
- get
- {
- return this.channelBindingToken;
- }
- }
- protected override bool CleanupChannelBinding
- {
- get
- {
- //client side channel binding token will be attached to the inbound response message also, so
- //we need to not clean up the CBT object for this HttpOutput object.
- return false;
- }
- }
- //Used to allow the channel binding object to be transferred to the
- //WebResponseHttpInput object.
- public override ChannelBinding TakeChannelBinding()
- {
- ChannelBinding result = this.channelBindingToken;
- this.channelBindingToken = null;
- return result;
- }
- protected override Stream GetOutputStream()
- {
- try
- {
- Stream outputStream;
- if (this.IsChannelBindingSupportEnabled)
- {
- TransportContext context;
- outputStream = httpWebRequest.GetRequestStream(out context);
- this.channelBindingToken = ChannelBindingUtility.GetToken(context);
- }
- else
- {
- outputStream = httpWebRequest.GetRequestStream();
- }
- outputStream = new WebRequestOutputStream(outputStream, httpWebRequest, this);
- return outputStream;
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, abortReason));
- }
- }
- protected override IAsyncResult BeginGetOutputStream(AsyncCallback callback, object state)
- {
- return new GetOutputStreamAsyncResult(httpWebRequest, this, callback, state);
- }
- protected override Stream EndGetOutputStream(IAsyncResult result)
- {
- return GetOutputStreamAsyncResult.End(result, out this.channelBindingToken);
- }
- protected override bool PrepareHttpSend(Message message)
- {
- bool wasContentTypeSet = false;
- string action = message.Headers.Action;
- if (action != null)
- {
- //This code is calling UrlPathEncode due to MessageBus bug 53362.
- //After reviewing this decision, we
- //feel that this was probably the wrong thing to do because UrlPathEncode
- //doesn't escape some characters like '+', '%', etc. The real issue behind
- //bug 53362 may have been as simple as being encoded multiple times on the client
- //but being decoded one time on the server. Calling UrlEncode would correctly
- //escape these characters, but since we don't want to break any customers and no
- //customers have complained, we will leave this as is for now...
- action = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", UrlUtility.UrlPathEncode(action));
- }
- bool suppressEntityBody = base.PrepareHttpSend(message);
- object property;
- if (message.Properties.TryGetValue(HttpRequestMessageProperty.Name, out property))
- {
- HttpRequestMessageProperty requestProperty = (HttpRequestMessageProperty)property;
- httpWebRequest.Method = requestProperty.Method;
- // Query string was applied in HttpChannelFactory.ApplyManualAddressing
- WebHeaderCollection requestHeaders = requestProperty.Headers;
- suppressEntityBody = suppressEntityBody || requestProperty.SuppressEntityBody;
- for (int i = 0; i < requestHeaders.Count; i++)
- {
- string name = requestHeaders.Keys[i];
- string value = requestHeaders[i];
- if (string.Compare(name, "accept", StringComparison.OrdinalIgnoreCase) == 0)
- {
- httpWebRequest.Accept = value;
- }
- else if (string.Compare(name, "connection", StringComparison.OrdinalIgnoreCase) == 0)
- {
- if (value.IndexOf("keep-alive", StringComparison.OrdinalIgnoreCase) != -1)
- {
- httpWebRequest.KeepAlive = true;
- }
- else
- {
- httpWebRequest.Connection = value;
- }
- }
- else if (string.Compare(name, "SOAPAction", StringComparison.OrdinalIgnoreCase) == 0)
- {
- if (action == null)
- {
- action = value;
- }
- else
- {
- if (value.Length > 0 && string.Compare(value, action, StringComparison.Ordinal) != 0)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.HttpSoapActionMismatch, action, value)));
- }
- }
- }
- else if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0)
- {
- // this will be taken care of by System.Net when we write to the content
- }
- else if (string.Compare(name, "content-type", StringComparison.OrdinalIgnoreCase) == 0)
- {
- httpWebRequest.ContentType = value;
- wasContentTypeSet = true;
- }
- else if (string.Compare(name, "expect", StringComparison.OrdinalIgnoreCase) == 0)
- {
- if (value.ToUpperInvariant().IndexOf("100-CONTINUE", StringComparison.OrdinalIgnoreCase) != -1)
- {
- httpWebRequest.ServicePoint.Expect100Continue = true;
- }
- else
- {
- httpWebRequest.Expect = value;
- }
- }
- else if (string.Compare(name, "host", StringComparison.OrdinalIgnoreCase) == 0)
- {
- // this should be controlled through Via
- }
- else if (string.Compare(name, "referer", StringComparison.OrdinalIgnoreCase) == 0)
- {
- // referrer is proper spelling, but referer is the what is in the protocol.
- httpWebRequest.Referer = value;
- }
- else if (string.Compare(name, "transfer-encoding", StringComparison.OrdinalIgnoreCase) == 0)
- {
- if (value.ToUpperInvariant().IndexOf("CHUNKED", StringComparison.OrdinalIgnoreCase) != -1)
- {
- httpWebRequest.SendChunked = true;
- }
- else
- {
- httpWebRequest.TransferEncoding = value;
- }
- }
- else if (string.Compare(name, "user-agent", StringComparison.OrdinalIgnoreCase) == 0)
- {
- httpWebRequest.UserAgent = value;
- }
- else if (string.Compare(name, "if-modified-since", StringComparison.OrdinalIgnoreCase) == 0)
- {
- DateTime modifiedSinceDate;
- if (DateTime.TryParse(value, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeLocal, out modifiedSinceDate))
- {
- httpWebRequest.IfModifiedSince = modifiedSinceDate;
- }
- else
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.HttpIfModifiedSinceParseError, value)));
- }
- }
- else if (string.Compare(name, "date", StringComparison.OrdinalIgnoreCase) == 0)
- {
- // this will be taken care of by System.Net when we make the request
- }
- else if (string.Compare(name, "proxy-connection", StringComparison.OrdinalIgnoreCase) == 0)
- {
- // set by System.Net if using a proxy.
- }
- else if (string.Compare(name, "range", StringComparison.OrdinalIgnoreCase) == 0)
- {
- // we don't support ranges in v1.
- }
- else
- {
- httpWebRequest.Headers.Add(name, value);
- }
- }
- }
- if (action != null)
- {
- if (message.Version.Envelope == EnvelopeVersion.Soap11)
- {
- httpWebRequest.Headers["SOAPAction"] = action;
- }
- else if (message.Version.Envelope == EnvelopeVersion.Soap12)
- {
- if (message.Version.Addressing == AddressingVersion.None)
- {
- bool shouldSetContentType = true;
- if (wasContentTypeSet)
- {
- if (httpWebRequest.ContentType.Contains("action")
- || httpWebRequest.ContentType.ToUpperInvariant().IndexOf("ACTION", StringComparison.OrdinalIgnoreCase) != -1)
- {
- try
- {
- ContentType parsedContentType = new ContentType(httpWebRequest.ContentType);
- if (parsedContentType.Parameters.ContainsKey("action"))
- {
- string value = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", parsedContentType.Parameters["action"]);
- if (string.Compare(value, action, StringComparison.Ordinal) != 0)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.HttpSoapActionMismatchContentType, action, value)));
- }
- shouldSetContentType = false;
- }
- }
- catch (FormatException formatException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.HttpContentTypeFormatException, formatException.Message, httpWebRequest.ContentType), formatException));
- }
- }
- }
- if (shouldSetContentType)
- {
- httpWebRequest.ContentType = string.Format(CultureInfo.InvariantCulture, "{0}; action={1}", httpWebRequest.ContentType, action);
- }
- }
- }
- else if (message.Version.Envelope != EnvelopeVersion.None)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new ProtocolException(SR.GetString(SR.EnvelopeVersionUnknown,
- message.Version.Envelope.ToString())));
- }
- }
- // since we don't get the output stream in send when retVal == true,
- // we need to disable chunking for some verbs (DELETE/PUT)
- if (suppressEntityBody)
- {
- httpWebRequest.SendChunked = false;
- }
- else if (this.IsChannelBindingSupportEnabled)
- {
- //force chunked upload since the length of the message is unknown before encoding.
- httpWebRequest.SendChunked = true;
- }
- return suppressEntityBody;
- }
- protected override void PrepareHttpSendCore(HttpResponseMessage message)
- {
- // HTTP pipeline for client side is not implemented yet
- // DCR CSDMain 216853 is tracking this
- Fx.Assert(false, "HTTP pipeline for client is not implemented yet. This method should not be called.");
- }
- class GetOutputStreamAsyncResult : AsyncResult
- {
- static AsyncCallback onGetRequestStream = Fx.ThunkCallback(new AsyncCallback(OnGetRequestStream));
- HttpOutput httpOutput;
- HttpWebRequest httpWebRequest;
- Stream outputStream;
- ChannelBinding channelBindingToken;
- public GetOutputStreamAsyncResult(HttpWebRequest httpWebRequest, HttpOutput httpOutput, AsyncCallback callback, object state)
- : base(callback, state)
- {
- this.httpWebRequest = httpWebRequest;
- this.httpOutput = httpOutput;
- IAsyncResult result = null;
- try
- {
- result = httpWebRequest.BeginGetRequestStream(onGetRequestStream, this);
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
- }
- if (result.CompletedSynchronously)
- {
- CompleteGetRequestStream(result);
- base.Complete(true);
- }
- }
- void CompleteGetRequestStream(IAsyncResult result)
- {
- try
- {
- TransportContext context;
- this.outputStream = new WebRequestOutputStream(httpWebRequest.EndGetRequestStream(result, out context), httpWebRequest, this.httpOutput);
- this.channelBindingToken = ChannelBindingUtility.GetToken(context);
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
- }
- }
- public static Stream End(IAsyncResult result, out ChannelBinding channelBindingToken)
- {
- GetOutputStreamAsyncResult thisPtr = AsyncResult.End<GetOutputStreamAsyncResult>(result);
- channelBindingToken = thisPtr.channelBindingToken;
- return thisPtr.outputStream;
- }
- static void OnGetRequestStream(IAsyncResult result)
- {
- if (result.CompletedSynchronously)
- return;
- GetOutputStreamAsyncResult thisPtr = (GetOutputStreamAsyncResult)result.AsyncState;
- Exception completionException = null;
- try
- {
- thisPtr.CompleteGetRequestStream(result);
- }
- #pragma warning suppress 56500 // [....], transferring exception to another thread
- catch (Exception e)
- {
- if (Fx.IsFatal(e))
- {
- throw;
- }
- completionException = e;
- }
- thisPtr.Complete(false, completionException);
- }
- }
- class WebRequestOutputStream : BytesReadPositionStream
- {
- HttpWebRequest httpWebRequest;
- HttpOutput httpOutput;
- int bytesSent = 0;
- public WebRequestOutputStream(Stream requestStream, HttpWebRequest httpWebRequest, HttpOutput httpOutput)
- : base(requestStream)
- {
- this.httpWebRequest = httpWebRequest;
- this.httpOutput = httpOutput;
- }
- public override void Close()
- {
- try
- {
- base.Close();
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
- }
- }
- public override long Position
- {
- get
- {
- return bytesSent;
- }
- set
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupported)));
- }
- }
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
- {
- this.bytesSent += count;
- try
- {
- return base.BeginWrite(buffer, offset, count, callback, state);
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
- }
- }
- public override void EndWrite(IAsyncResult result)
- {
- try
- {
- base.EndWrite(result);
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
- }
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- try
- {
- base.Write(buffer, offset, count);
- }
- catch (ObjectDisposedException objectDisposedException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestCanceledException(objectDisposedException, httpWebRequest, httpOutput.abortReason));
- }
- catch (IOException ioException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestIOException(ioException, httpWebRequest));
- }
- catch (WebException webException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(HttpChannelUtilities.CreateRequestWebException(webException, httpWebRequest, httpOutput.abortReason));
- }
- this.bytesSent += count;
- }
- }
- }
- class ListenerResponseHttpOutput : HttpOutput
- {
- HttpListenerResponse listenerResponse;
- string httpMethod;
- [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.Usage, "CA2214", Justification = "No one else is inhiriting from this class.")]
- public ListenerResponseHttpOutput(HttpListenerResponse listenerResponse, IHttpTransportFactorySettings settings, Message message, string httpMethod)
- : base(settings, message, false, true)
- {
- this.listenerResponse = listenerResponse;
- this.httpMethod = httpMethod;
- if (message.IsFault)
- {
- this.SetStatusCode(HttpStatusCode.InternalServerError);
- }
- else
- {
- this.SetStatusCode(HttpStatusCode.OK);
- }
- }
- protected override string HttpMethod
- {
- get { return this.httpMethod; }
- }
- public override void Abort(HttpAbortReason abortReason)
- {
- listenerResponse.Abort();
- base.Abort(abortReason);
- }
- protected override void AddMimeVersion(string version)
- {
- listenerResponse.Headers[HttpChannelUtilities.MIMEVersionHeader] = version;
- }
- protected override bool PrepareHttpSend(Message message)
- {
- bool result = base.PrepareHttpSend(message);
- if (this.CanSendCompressedResponses)
- {
- string contentType = this.listenerResponse.ContentType;
- string contentEncoding;
- if (HttpChannelUtilities.GetHttpResponseTypeAndEncodingForCompression(ref contentType, out contentEncoding))
- {
- if (contentType != this.listenerResponse.ContentType)
- {
- this.SetContentType(contentType);
- }
- this.SetContentEncoding(contentEncoding);
- }
- }
- HttpResponseMessageProperty responseProperty = message.Properties.GetValue<HttpResponseMessageProperty>(HttpResponseMessageProperty.Name, true);
- bool httpResponseMessagePropertyFound = responseProperty != null;
- bool httpMethodIsHead = string.Compare(this.httpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) == 0;
- if (httpMethodIsHead ||
- httpResponseMessagePropertyFound && responseProperty.SuppressEntityBody)
- {
- result = true;
- this.SetContentLength(0);
- this.SetContentType(null);
- listenerResponse.SendChunked = false;
- }
- if (httpResponseMessagePropertyFound)
- {
- this.SetStatusCode(responseProperty.StatusCode);
- if (responseProperty.StatusDescription != null)
- {
- this.SetStatusDescription(responseProperty.StatusDescription);
- }
- WebHeaderCollection responseHeaders = responseProperty.Headers;
- for (int i = 0; i < responseHeaders.Count; i++)
- {
- string name = responseHeaders.Keys[i];
- string value = responseHeaders[i];
- if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0)
- {
- int contentLength = -1;
- if (httpMethodIsHead &&
- int.TryParse(value, out contentLength))
- {
- this.SetContentLength(contentLength);
- }
- // else
- //this will be taken care of by System.Net when we write to the content
- }
- else if (string.Compare(name, "content-type", StringComparison.OrdinalIgnoreCase) == 0)
- {
- if (httpMethodIsHead ||
- !responseProperty.SuppressEntityBody)
- {
- this.SetContentType(value);
- }
- }
- else
- {
- this.AddHeader(name, value);
- }
- }
- }
- return result;
- }
- protected override void PrepareHttpSendCore(HttpResponseMessage message)
- {
- this.listenerResponse.StatusCode = (int)message.StatusCode;
- if (message.ReasonPhrase != null)
- {
- this.listenerResponse.StatusDescription = message.ReasonPhrase;
- }
- HttpChannelUtilities.CopyHeaders(message, AddHeader);
- }
- protected override void AddHeader(string name, string value)
- {
- if (string.Compare(name, "WWW-Authenticate", StringComparison.OrdinalIgnoreCase) == 0)
- {
- listenerResponse.AddHeader(name, value);
- }
- else
- {
- listenerResponse.AppendHeader(name, value);
- }
- }
- protected override void SetContentType(string contentType)
- {
- listenerResponse.ContentType = contentType;
- }
- protected override void SetContentEncoding(string contentEncoding)
- {
- this.listenerResponse.AddHeader(HttpChannelUtilities.ContentEncodingHeader, contentEncoding);
- }
- protected override void SetContentLength(int contentLength)
- {
- listenerResponse.ContentLength64 = contentLength;
- }
- protected override void SetStatusCode(HttpStatusCode statusCode)
- {
- listenerResponse.StatusCode = (int)statusCode;
- }
- protected override void SetStatusDescription(string statusDescription)
- {
- listenerResponse.StatusDescription = statusDescription;
- }
- protected override Stream GetOutputStream()
- {
- return new ListenerResponseOutputStream(listenerResponse);
- }
- class ListenerResponseOutputStream : BytesReadPositionStream
- {
- public ListenerResponseOutputStream(HttpListenerResponse listenerResponse)
- : base(listenerResponse.OutputStream)
- {
- }
- public override void Close()
- {
- try
- {
- base.Close();
- }
- catch (HttpListenerException listenerException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- HttpChannelUtilities.CreateCommunicationException(listenerException));
- }
- }
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
- {
- try
- {
- return base.BeginWrite(buffer, offset, count, callback, state);
- }
- catch (HttpListenerException listenerException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- HttpChannelUtilities.CreateCommunicationException(listenerException));
- }
- catch (ApplicationException applicationException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
- applicationException));
- }
- }
- public override void EndWrite(IAsyncResult result)
- {
- try
- {
- base.EndWrite(result);
- }
- catch (HttpListenerException listenerException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- HttpChannelUtilities.CreateCommunicationException(listenerException));
- }
- catch (ApplicationException applicationException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
- applicationException));
- }
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- try
- {
- base.Write(buffer, offset, count);
- }
- catch (HttpListenerException listenerException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- HttpChannelUtilities.CreateCommunicationException(listenerException));
- }
- catch (ApplicationException applicationException)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted),
- applicationException));
- }
- }
- }
- }
- }
- enum HttpAbortReason
- {
- None,
- Aborted,
- TimedOut
- }
- delegate void AddHeaderDelegate(string headerName, string headerValue);
- static class HttpChannelUtilities
- {
- internal static class StatusDescriptionStrings
- {
- internal const string HttpContentTypeMissing = "Missing Content Type";
- internal const string HttpContentTypeMismatch = "Cannot process the message because the content type '{0}' was not the expected type '{1}'.";
- internal const string HttpStatusServiceActivationException = "System.ServiceModel.ServiceActivationException";
- }
- internal static class ObsoleteDescriptionStrings
- {
- internal const string PropertyObsoleteUseAllowCookies = "This property is obsolete. To enable Http CookieContainer, use the AllowCookies property instead.";
- internal const string TypeObsoleteUseAllowCookies = "This type is obsolete. To enable the Http CookieContainer, use the AllowCookies property on the http binding or on the HttpTransportBindingElement.";
- }
- internal const string HttpStatusCodeKey = "HttpStatusCode";
- internal const string HttpStatusCodeExceptionKey = "System.ServiceModel.Channels.HttpInput.HttpStatusCode";
- internal const string HttpStatusDescriptionExceptionKey = "System.ServiceModel.Channels.HttpInput.HttpStatusDescription";
- internal const int ResponseStreamExcerptSize = 1024;
- internal const string MIMEVersionHeader = "MIME-Version";
- internal const string ContentEncodingHeader = "Content-Encoding";
- internal const string AcceptEncodingHeader = "Accept-Encoding";
- private const string ContentLengthHeader = "Content-Length";
- private static readonly HashSet<string> httpContentHeaders = new HashSet<string>()
- {
- "Allow", "Content-Encoding", "Content-Language", "Content-Location", "Content-MD5",
- "Content-Range", "Expires", "Last-Modified", "Content-Type", ContentLengthHeader
- };
- static bool allReferencedAssembliesLoaded = false;
- public static Exception CreateCommunicationException(HttpListenerException listenerException)
- {
- switch (listenerException.NativeErrorCode)
- {
- case UnsafeNativeMethods.ERROR_NO_TRACKING_SERVICE:
- return new CommunicationException(SR.GetString(SR.HttpNoTrackingService, listenerException.Message), listenerException);
- case UnsafeNativeMethods.ERROR_NETNAME_DELETED:
- return new CommunicationException(SR.GetString(SR.HttpNetnameDeleted, listenerException.Message), listenerException);
- case UnsafeNativeMethods.ERROR_INVALID_HANDLE:
- return new CommunicationObjectAbortedException(SR.GetString(SR.HttpResponseAborted), listenerException);
- case UnsafeNativeMethods.ERROR_NOT_ENOUGH_MEMORY:
- case UnsafeNativeMethods.ERROR_OUTOFMEMORY:
- case UnsafeNativeMethods.ERROR_NO_SYSTEM_RESOURCES:
- return new InsufficientMemoryException(SR.GetString(SR.InsufficentMemory), listenerException);
- default:
- return new CommunicationException(listenerException.Message, listenerException);
- }
- }
- public static void EnsureHttpRequestMessageContentNotNull(HttpRequestMessage httpRequestMessage)
- {
- if (httpRequestMessage.Content == null)
- {
- httpRequestMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
- }
- }
- public static void EnsureHttpResponseMessageContentNotNull(HttpResponseMessage httpResponseMessage)
- {
- if (httpResponseMessage.Content == null)
- {
- httpResponseMessage.Content = new ByteArrayContent(EmptyArray<byte>.Instance);
- }
- }
- public static bool IsEmpty(HttpResponseMessage httpResponseMessage)
- {
- return httpResponseMessage.Content == null
- || (httpResponseMessage.Content.Headers.ContentLength.HasValue && httpResponseMessage.Content.Headers.ContentLength.Value == 0);
- }
- internal static void HandleContinueWithTask(Task task)
- {
- HandleContinueWithTask(task, null);
- }
- internal static void HandleContinueWithTask(Task task, Action<Exception> exceptionHandler)
- {
- if (task.IsFaulted)
- {
- if (exceptionHandler == null)
- {
- throw FxTrace.Exception.AsError<FaultException>(task.Exception);
- }
- else
- {
- exceptionHandler.Invoke(task.Exception);
- }
- }
- else if (task.IsCanceled)
- {
- throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.TaskCancelledError)));
- }
- }
- public static void AbortRequest(HttpWebRequest request)
- {
- request.Abort();
- }
- public static void SetRequestTimeout(HttpWebRequest request, TimeSpan timeout)
- {
- int millisecondsTimeout = TimeoutHelper.ToMilliseconds(timeout);
- if (millisecondsTimeout == 0)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(SR.GetString(
- SR.HttpRequestTimedOut, request.RequestUri, timeout)));
- }
- request.Timeout = millisecondsTimeout;
- request.ReadWriteTimeout = millisecondsTimeout;
- }
- public static void AddReplySecurityProperty(HttpChannelFactory<IRequestChannel> factory, HttpWebRequest webRequest,
- HttpWebResponse webResponse, Message replyMessage)
- {
- SecurityMessageProperty securityProperty = factory.CreateReplySecurityProperty(webRequest, webResponse);
- if (securityProperty != null)
- {
- replyMessage.Properties.Security = securityProperty;
- }
- }
- public static void CopyHeaders(HttpRequestMessage request, AddHeaderDelegate addHeader)
- {
- HttpChannelUtilities.CopyHeaders(request.Headers, addHeader);
- if (request.Content != null)
- {
- HttpChannelUtilities.CopyHeaders(request.Content.Headers, addHeader);
- }
- }
- public static void CopyHeaders(HttpResponseMessage response, AddHeaderDelegate addHeader)
- {
- HttpChannelUtilities.CopyHeaders(response.Headers, addHeader);
- if (response.Content != null)
- {
- HttpChannelUtilities.CopyHeaders(response.Content.Headers, addHeader);
- }
- }
- static void CopyHeaders(HttpHeaders headers, AddHeaderDelegate addHeader)
- {
- foreach (KeyValuePair<string, IEnumerable<string>> header in headers)
- {
- foreach (string value in header.Value)
- {
- TryAddToCollection(addHeader, header.Key, value);
- }
- }
- }
- public static void CopyHeaders(NameValueCollection headers, AddHeaderDelegate addHeader)
- {
- //this nested loop logic was copied from NameValueCollection.Add(NameValueCollection)
- int count = headers.Count;
- for (int i = 0; i < count; i++)
- {
- string key = headers.GetKey(i);
- string[] values = headers.GetValues(i);
- if (values != null)
- {
- for (int j = 0; j < values.Length; j++)
- {
- TryAddToCollection(addHeader, key, values[j]);
- }
- }
- else
- {
- addHeader(key, null);
- }
- }
- }
- public static void CopyHeadersToNameValueCollection(NameValueCollection headers, NameValueCollection destination)
- {
- CopyHeaders(headers, destination.Add);
- }
- [System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
- Justification = "The exceptions are traced already.")]
- static void TryAddToCollection(AddHeaderDelegate addHeader, string headerName, string value)
- {
- try
- {
- addHeader(headerName, value);
- }
- catch (ArgumentException ex)
- {
- string encodedValue = null;
- if (TryEncodeHeaderValueAsUri(headerName, value, out encodedValue))
- {
- //note: if the hosthame of a referer header contains illegal chars, we will still throw from here
- //because Uri will not fix this up for us, which is ok. The request will get rejected in the error code path.
- addHeader(headerName, encodedValue);
- }
- else
- {
- // In self-hosted scenarios, some of the headers like Content-Length cannot be added directly.
- // It will throw ArgumentException instead.
- FxTrace.Exception.AsInformation(ex);
- }
- }
- }
- static bool TryEncodeHeaderValueAsUri(string headerName, string value, out string result)
- {
- result = null;
- //Internet Explorer will send the referrer header on the wire in unicode without encoding it
- //this will cause errors when added to a WebHeaderCollection. This is a workaround for sharepoint,
- //but will only work for WebHosted Scenarios.
- if (String.Compare(headerName, "Referer", StringComparison.OrdinalIgnoreCase) == 0)
- {
- Uri uri;
- if (Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out uri))
- {
- if (uri.IsAbsoluteUri)
- {
- result = uri.AbsoluteUri;
- }
- else
- {
- result = uri.GetComponents(UriComponents.SerializationInfoString, UriFormat.UriEscaped);
- }
- return true;
- }
- }
- return false;
- }
- //
- internal static Type GetTypeFromAssembliesInCurrentDomain(string typeString)
- {
- Type type = Type.GetType(typeString, false);
- if (null == type)
- {
- if (!allReferencedAssembliesLoaded)
- {
- allReferencedAssembliesLoaded = true;
- AspNetEnvironment.Current.EnsureAllReferencedAssemblyLoaded();
- }
- Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
- for (int i = 0; i < assemblies.Length; i++)
- {
- type = assemblies[i].GetType(typeString, false);
- if (null != type)
- {
- break;
- }
- }
- }
- return type;
- }
- public static NetworkCredential GetCredential(AuthenticationSchemes authenticationScheme,
- SecurityTokenProviderContainer credentialProvider, TimeSpan timeout,
- out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel)
- {
- impersonationLevel = TokenImpersonationLevel.None;
- authenticationLevel = AuthenticationLevel.None;
- NetworkCredential result = null;
- if (authenticationScheme != AuthenticationSchemes.Anonymous)
- {
- result = GetCredentialCore(authenticationScheme, credentialProvider, timeout, out impersonationLevel, out authenticationLevel);
- }
- return result;
- }
- [MethodImpl(MethodImplOptions.NoInlining)]
- static NetworkCredential GetCredentialCore(AuthenticationSchemes authenticationScheme,
- SecurityTokenProviderContainer credentialProvider, TimeSpan timeout,
- out TokenImpersonationLevel impersonationLevel, out AuthenticationLevel authenticationLevel)
- {
- impersonationLevel = TokenImpersonationLevel.None;
- authenticationLevel = AuthenticationLevel.None;
- NetworkCredential result = null;
- switch (authenticationScheme)
- {
- case AuthenticationSchemes.Basic:
- result = TransportSecurityHelpers.GetUserNameCredential(credentialProvider, timeout);
- impersonationLevel = TokenImpersonationLevel.Delegation;
- break;
- case AuthenticationSchemes.Digest:
- result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
- out impersonationLevel, out authenticationLevel);
- HttpChannelUtilities.ValidateDigestCredential(ref result, impersonationLevel);
- break;
- case AuthenticationSchemes.Negotiate:
- result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
- out impersonationLevel, out authenticationLevel);
- break;
- case AuthenticationSchemes.Ntlm:
- result = TransportSecurityHelpers.GetSspiCredential(credentialProvider, timeout,
- out impersonationLevel, out authenticationLevel);
- if (authenticationLevel == AuthenticationLevel.MutualAuthRequired)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- new InvalidOperationException(SR.GetString(SR.CredentialDisallowsNtlm)));
- }
- break;
- default:
- // The setter for this property should prevent this.
- throw Fx.AssertAndThrow("GetCredential: Invalid authentication scheme");
- }
- return result;
- }
- public static HttpWebResponse ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
- {
- HttpWebResponse response = null;
- if (webException.Status == WebExceptionStatus.Success ||
- webException.Status == WebExceptionStatus.ProtocolError)
- {
- response = (HttpWebResponse)webException.Response;
- }
- if (response == null)
- {
- Exception convertedException = ConvertWebException(webException, request, abortReason);
- if (convertedException != null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(convertedException);
- }
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(webException.Message,
- webException));
- }
- if (response.StatusCode == HttpStatusCode.NotFound)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException));
- }
- if (response.StatusCode == HttpStatusCode.ServiceUnavailable)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServerTooBusyException(SR.GetString(SR.HttpServerTooBusy, request.RequestUri.AbsoluteUri), webException));
- }
- if (response.StatusCode == HttpStatusCode.UnsupportedMediaType)
- {
- string statusDescription = response.StatusDescription;
- if (!string.IsNullOrEmpty(statusDescription))
- {
- if (string.Compare(statusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpContentTypeMissing, StringComparison.OrdinalIgnoreCase) == 0)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.MissingContentType, request.RequestUri), webException));
- }
- }
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.FramingContentTypeMismatch, request.ContentType, request.RequestUri), webException));
- }
- if (response.StatusCode == HttpStatusCode.GatewayTimeout)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new TimeoutException(webException.Message, webException));
- }
- // if http.sys has a request queue on the TCP port, then if the path fails to match it will send
- // back "<h1>Bad Request (Invalid Hostname)</h1>" in the body of a 400 response.
- // See code at \\index1\sddnsrv\net\http\sys\httprcv.c for details
- if (response.StatusCode == HttpStatusCode.BadRequest)
- {
- const string httpSysRequestQueueNotFound = "<h1>Bad Request (Invalid Hostname)</h1>";
- const string httpSysRequestQueueNotFoundVista = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\"http://www.w3.org/TR/html4/strict.dtd\">\r\n<HTML><HEAD><TITLE>Bad Request</TITLE>\r\n<META HTTP-EQUIV=\"Content-Type\" Content=\"text/html; charset=us-ascii\"></HEAD>\r\n<BODY><h2>Bad Request - Invalid Hostname</h2>\r\n<hr><p>HTTP Error 400. The request hostname is invalid.</p>\r\n</BODY></HTML>\r\n";
- string notFoundTestString = null;
- if (response.ContentLength == httpSysRequestQueueNotFound.Length)
- {
- notFoundTestString = httpSysRequestQueueNotFound;
- }
- else if (response.ContentLength == httpSysRequestQueueNotFoundVista.Length)
- {
- notFoundTestString = httpSysRequestQueueNotFoundVista;
- }
- if (notFoundTestString != null)
- {
- Stream responseStream = response.GetResponseStream();
- byte[] responseBytes = new byte[notFoundTestString.Length];
- int bytesRead = responseStream.Read(responseBytes, 0, responseBytes.Length);
- // since the response is buffered by System.Net (it's an error response), we should have read
- // the amount we were expecting
- if (bytesRead == notFoundTestString.Length
- && notFoundTestString == UTF8Encoding.ASCII.GetString(responseBytes))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException));
- }
- }
- }
- return response;
- }
- public static Exception ConvertWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
- {
- switch (webException.Status)
- {
- case WebExceptionStatus.ConnectFailure:
- case WebExceptionStatus.NameResolutionFailure:
- case WebExceptionStatus.ProxyNameResolutionFailure:
- return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException);
- case WebExceptionStatus.SecureChannelFailure:
- return new SecurityNegotiationException(SR.GetString(SR.SecureChannelFailure, request.RequestUri.Authority), webException);
- case WebExceptionStatus.TrustFailure:
- return new SecurityNegotiationException(SR.GetString(SR.TrustFailure, request.RequestUri.Authority), webException);
- case WebExceptionStatus.Timeout:
- return new TimeoutException(CreateRequestTimedOutMessage(request), webException);
- case WebExceptionStatus.ReceiveFailure:
- return new CommunicationException(SR.GetString(SR.HttpReceiveFailure, request.RequestUri), webException);
- case WebExceptionStatus.SendFailure:
- return new CommunicationException(SR.GetString(SR.HttpSendFailure, request.RequestUri), webException);
- case WebExceptionStatus.RequestCanceled:
- return CreateRequestCanceledException(webException, request, abortReason);
- case WebExceptionStatus.ProtocolError:
- HttpWebResponse response = (HttpWebResponse)webException.Response;
- Fx.Assert(response != null, "'response' MUST NOT be NULL for WebExceptionStatus=='ProtocolError'.");
- if (response.StatusCode == HttpStatusCode.InternalServerError &&
- string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0)
- {
- return new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri));
- }
- else
- {
- return null;
- }
- default:
- return null;
- }
- }
- public static Exception CreateResponseIOException(IOException ioException, TimeSpan receiveTimeout)
- {
- if (ioException.InnerException is SocketException)
- {
- return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, receiveTimeout, ioException);
- }
- return new CommunicationException(SR.GetString(SR.HttpTransferError, ioException.Message), ioException);
- }
- public static Exception CreateResponseWebException(WebException webException, HttpWebResponse response)
- {
- switch (webException.Status)
- {
- case WebExceptionStatus.RequestCanceled:
- return TraceResponseException(new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, response.ResponseUri), webException));
- case WebExceptionStatus.ConnectionClosed:
- return TraceResponseException(new CommunicationException(webException.Message, webException));
- case WebExceptionStatus.Timeout:
- return TraceResponseException(new TimeoutException(SR.GetString(SR.HttpResponseTimedOut, response.ResponseUri,
- TimeSpan.FromMilliseconds(response.GetResponseStream().ReadTimeout)), webException));
- default:
- return CreateUnexpectedResponseException(webException, response);
- }
- }
- public static Exception CreateRequestCanceledException(Exception webException, HttpWebRequest request, HttpAbortReason abortReason)
- {
- switch (abortReason)
- {
- case HttpAbortReason.Aborted:
- return new CommunicationObjectAbortedException(SR.GetString(SR.HttpRequestAborted, request.RequestUri), webException);
- case HttpAbortReason.TimedOut:
- return new TimeoutException(CreateRequestTimedOutMessage(request), webException);
- default:
- return new CommunicationException(SR.GetString(SR.HttpTransferError, webException.Message), webException);
- }
- }
- public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request)
- {
- return CreateRequestIOException(ioException, request, null);
- }
- public static Exception CreateRequestIOException(IOException ioException, HttpWebRequest request, Exception originalException)
- {
- Exception exception = originalException == null ? ioException : originalException;
- if (ioException.InnerException is SocketException)
- {
- return SocketConnection.ConvertTransferException((SocketException)ioException.InnerException, TimeSpan.FromMilliseconds(request.Timeout), exception);
- }
- return new CommunicationException(SR.GetString(SR.HttpTransferError, exception.Message), exception);
- }
- static string CreateRequestTimedOutMessage(HttpWebRequest request)
- {
- return SR.GetString(SR.HttpRequestTimedOut, request.RequestUri, TimeSpan.FromMilliseconds(request.Timeout));
- }
- public static Exception CreateRequestWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
- {
- Exception convertedException = ConvertWebException(webException, request, abortReason);
- if (webException.Response != null)
- {
- //free the connection for use by another request
- webException.Response.Close();
- }
- if (convertedException != null)
- {
- return convertedException;
- }
- if (webException.InnerException is IOException)
- {
- return CreateRequestIOException((IOException)webException.InnerException, request, webException);
- }
- if (webException.InnerException is SocketException)
- {
- return SocketConnectionInitiator.ConvertConnectException((SocketException)webException.InnerException, request.RequestUri, TimeSpan.MaxValue, webException);
- }
- return new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, request.RequestUri.AbsoluteUri), webException);
- }
- static Exception CreateUnexpectedResponseException(WebException responseException, HttpWebResponse response)
- {
- string statusDescription = response.StatusDescription;
- if (string.IsNullOrEmpty(statusDescription))
- statusDescription = response.StatusCode.ToString();
- return TraceResponseException(
- new ProtocolException(SR.GetString(SR.UnexpectedHttpResponseCode,
- (int)response.StatusCode, statusDescription), responseException));
- }
- public static Exception CreateNullReferenceResponseException(NullReferenceException nullReferenceException)
- {
- return TraceResponseException(
- new ProtocolException(SR.GetString(SR.NullReferenceOnHttpResponse), nullReferenceException));
- }
- static string GetResponseStreamString(HttpWebResponse webResponse, out int bytesRead)
- {
- Stream responseStream = webResponse.GetResponseStream();
- long bufferSize = webResponse.ContentLength;
- if (bufferSize < 0 || bufferSize > ResponseStreamExcerptSize)
- {
- bufferSize = ResponseStreamExcerptSize;
- }
- byte[] responseBuffer = DiagnosticUtility.Utility.AllocateByteArray(checked((int)bufferSize));
- bytesRead = responseStream.Read(responseBuffer, 0, (int)bufferSize);
- responseStream.Close();
- return System.Text.Encoding.UTF8.GetString(responseBuffer, 0, bytesRead);
- }
- static Exception TraceResponseException(Exception exception)
- {
- if (DiagnosticUtility.ShouldTraceError)
- {
- TraceUtility.TraceEvent(TraceEventType.Error, TraceCode.HttpChannelUnexpectedResponse, SR.GetString(SR.TraceCodeHttpChannelUnexpectedResponse), (object)null, exception);
- }
- return exception;
- }
- static bool ValidateEmptyContent(HttpWebResponse response)
- {
- bool responseIsEmpty = true;
- if (response.ContentLength > 0)
- {
- responseIsEmpty = false;
- }
- else if (response.ContentLength == -1) // chunked
- {
- Stream responseStream = response.GetResponseStream();
- byte[] testBuffer = new byte[1];
- responseIsEmpty = (responseStream.Read(testBuffer, 0, 1) != 1);
- }
- return responseIsEmpty;
- }
- static void ValidateAuthentication(HttpWebRequest request, HttpWebResponse response,
- WebException responseException, HttpChannelFactory<IRequestChannel> factory)
- {
- if (response.StatusCode == HttpStatusCode.Unauthorized)
- {
- string message = SR.GetString(SR.HttpAuthorizationFailed, factory.AuthenticationScheme,
- response.Headers[HttpResponseHeader.WwwAuthenticate]);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- TraceResponseException(new MessageSecurityException(message, responseException)));
- }
- if (response.StatusCode == HttpStatusCode.Forbidden)
- {
- string message = SR.GetString(SR.HttpAuthorizationForbidden, factory.AuthenticationScheme);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- TraceResponseException(new MessageSecurityException(message, responseException)));
- }
- if ((request.AuthenticationLevel == AuthenticationLevel.MutualAuthRequired) &&
- !response.IsMutuallyAuthenticated)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
- TraceResponseException(new SecurityNegotiationException(SR.GetString(SR.HttpMutualAuthNotSatisfied),
- responseException)));
- }
- }
- public static void ValidateDigestCredential(ref NetworkCredential credential, TokenImpersonationLevel impersonationLevel)
- {
- // this is a work-around to VSWhidbey#470545 (Since the service always uses Impersonation,
- // we mitigate EOP by preemtively not allowing Identification)
- if (!SecurityUtils.IsDefaultNetworkCredential(credential))
- {
- // With a non-default credential, Digest will not honor a client impersonation constraint of
- // TokenImpersonationLevel.Identification.
- if (!TokenImpersonationLevelHelper.IsGreaterOrEqual(impersonationLevel,
- TokenImpersonationLevel.Impersonation))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(
- SR.DigestExplicitCredsImpersonationLevel, impersonationLevel)));
- }
- }
- }
- // only valid response codes are 500 (if it's a fault) or 200 (iff it's a response message)
- public static HttpInput ValidateRequestReplyResponse(HttpWebRequest request, HttpWebResponse response,
- HttpChannelFactory<IRequestChannel> factory, WebException responseException, ChannelBinding channelBinding)
- {
- ValidateAuthentication(request, response, responseException, factory);
- HttpInput httpInput = null;
- // We will close the HttpWebResponse if we got an error code betwen 200 and 300 and
- // 1) an exception was thrown out or
- // 2) it's an empty message and we are using SOAP.
- // For responses with status code above 300, System.Net will close the underlying connection so we don't need to worry about that.
- if ((200 <= (int)response.StatusCode && (int)response.StatusCode < 300) || response.StatusCode == HttpStatusCode.InternalServerError)
- {
- if (response.StatusCode == HttpStatusCode.InternalServerError
- && string.Compare(response.StatusDescription, HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException, StringComparison.OrdinalIgnoreCase) == 0)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ServiceActivationException(SR.GetString(SR.Hosting_ServiceActivationFailed, request.RequestUri)));
- }
- else
- {
- bool throwing = true;
- try
- {
- if (string.IsNullOrEmpty(response.ContentType))
- {
- if (!ValidateEmptyContent(response))
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException(
- new ProtocolException(
- SR.GetString(SR.HttpContentTypeHeaderRequired),
- responseException)));
- }
- }
- else if (response.ContentLength != 0)
- {
- MessageEncoder encoder = factory.MessageEncoderFactory.Encoder;
- if (!encoder.IsContentTypeSupported(response.ContentType))
- {
- int bytesRead;
- String responseExcerpt = GetResponseStreamString(response, out bytesRead);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(TraceResponseException(
- new ProtocolException(
- SR.GetString(
- SR.ResponseContentTypeMismatch,
- response.ContentType,
- encoder.ContentType,
- bytesRead,
- responseExcerpt), responseException)));
- }
- httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding);
- httpInput.WebException = responseException;
- }
- throwing = false;
- }
- finally
- {
- if (throwing)
- {
- response.Close();
- }
- }
- }
- if (httpInput == null)
- {
- if (factory.MessageEncoderFactory.MessageVersion == MessageVersion.None)
- {
- httpInput = HttpInput.CreateHttpInput(response, factory, channelBinding);
- httpInput.WebException = responseException;
- }
- else
- {
- // In this case, we got a response with
- // 1) status code between 200 and 300
- // 2) Non-empty Content Type string
- // 3) Zero content length
- // Since we are trying to use SOAP here, the message seems to be malicious and we should
- // just close the response directly.
- response.Close();
- }
- }
- }
- else
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateUnexpectedResponseException(responseException, response));
- }
- return httpInput;
- }
- public static bool GetHttpResponseTypeAndEncodingForCompression(ref string contentType, out string contentEncoding)
- {
- contentEncoding = null;
- bool isSession = false;
- bool isDeflate = false;
- if (string.Equals(BinaryVersion.GZipVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) ||
- (isSession = string.Equals(BinaryVersion.GZipVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase)) ||
- (isDeflate = (string.Equals(BinaryVersion.DeflateVersion1.ContentType, contentType, StringComparison.OrdinalIgnoreCase) ||
- (isSession = string.Equals(BinaryVersion.DeflateVersion1.SessionContentType, contentType, StringComparison.OrdinalIgnoreCase)))))
- {
- contentType = isSession ? BinaryVersion.Version1.SessionContentType : BinaryVersion.Version1.ContentType;
- contentEncoding = isDeflate ? MessageEncoderCompressionHandler.DeflateContentEncoding : MessageEncoderCompressionHandler.GZipContentEncoding;
- return true;
- }
- return false;
- }
- }
- abstract class HttpDelayedAcceptStream : DetectEofStream
- {
- HttpOutput httpOutput;
- bool isHttpOutputClosed;
- /// <summary>
- /// Indicates whether the HttpOutput should be closed when this stream is closed. In the streamed case,
- /// we�ll leave the HttpOutput opened (and it will be closed by the HttpRequestContext, so we won't leak it).
- /// </summary>
- bool closeHttpOutput;
- // sometimes we can't flush the HTTP output until we're done reading the end of the
- // incoming stream of the HTTP input
- protected HttpDelayedAcceptStream(Stream stream)
- : base(stream)
- {
- }
- public bool EnableDelayedAccept(HttpOutput output, bool closeHttpOutput)
- {
- if (IsAtEof)
- {
- return false;
- }
- this.closeHttpOutput = closeHttpOutput;
- this.httpOutput = output;
- return true;
- }
- protected override void OnReceivedEof()
- {
- if (this.closeHttpOutput)
- {
- CloseHttpOutput();
- }
- }
- public override void Close()
- {
- if (this.closeHttpOutput)
- {
- CloseHttpOutput();
- }
- base.Close();
- }
- void CloseHttpOutput()
- {
- if (this.httpOutput != null && !this.isHttpOutputClosed)
- {
- this.httpOutput.Close();
- this.isHttpOutputClosed = true;
- }
- }
- }
- abstract class BytesReadPositionStream : DelegatingStream
- {
- int bytesSent = 0;
- protected BytesReadPositionStream(Stream stream)
- : base(stream)
- {
- }
- public override long Position
- {
- get
- {
- return bytesSent;
- }
- set
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SeekNotSupported)));
- }
- }
- public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
- {
- this.bytesSent += count;
- return BaseStream.BeginWrite(buffer, offset, count, callback, state);
- }
- public override void Write(byte[] buffer, int offset, int count)
- {
- BaseStream.Write(buffer, offset, count);
- this.bytesSent += count;
- }
- public override void WriteByte(byte value)
- {
- BaseStream.WriteByte(value);
- this.bytesSent++;
- }
- }
- class PreReadStream : DelegatingStream
- {
- byte[] preReadBuffer;
- public PreReadStream(Stream stream, byte[] preReadBuffer)
- : base(stream)
- {
- this.preReadBuffer = preReadBuffer;
- }
- bool ReadFromBuffer(byte[] buffer, int offset, int count, out int bytesRead)
- {
- if (this.preReadBuffer != null)
- {
- if (buffer == null)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("buffer");
- }
- if (offset >= buffer.Length)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("offset", offset,
- SR.GetString(SR.OffsetExceedsBufferBound, buffer.Length - 1)));
- }
- if (count < 0)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("count", count,
- SR.GetString(SR.ValueMustBeNonNegative)));
- }
- if (count == 0)
- {
- bytesRead = 0;
- }
- else
- {
- buffer[offset] = this.preReadBuffer[0];
- this.preReadBuffer = null;
- bytesRead = 1;
- }
- return true;
- }
- bytesRead = -1;
- return false;
- }
- public override int Read(byte[] buffer, int offset, int count)
- {
- int bytesRead;
- if (ReadFromBuffer(buffer, offset, count, out bytesRead))
- {
- return bytesRead;
- }
- return base.Read(buffer, offset, count);
- }
- public override int ReadByte()
- {
- if (this.preReadBuffer != null)
- {
- byte[] tempBuffer = new byte[1];
- int bytesRead;
- if (ReadFromBuffer(tempBuffer, 0, 1, out bytesRead))
- {
- return tempBuffer[0];
- }
- }
- return base.ReadByte();
- }
- public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
- {
- int bytesRead;
- if (ReadFromBuffer(buffer, offset, count, out bytesRead))
- {
- return new CompletedAsyncResult<int>(bytesRead, callback, state);
- }
- return base.BeginRead(buffer, offset, count, callback, state);
- }
- public override int EndRead(IAsyncResult result)
- {
- if (result is CompletedAsyncResult<int>)
- {
- return CompletedAsyncResult<int>.End(result);
- }
- else
- {
- return base.EndRead(result);
- }
- }
- }
- class HttpRequestMessageHttpInput : HttpInput, HttpRequestMessageProperty.IHttpHeaderProvider
- {
- const string SoapAction = "SOAPAction";
- HttpRequestMessage httpRequestMessage;
- ChannelBinding channelBinding;
- public HttpRequestMessageHttpInput(HttpRequestMessage httpRequestMessage, IHttpTransportFactorySettings settings, bool enableChannelBinding, ChannelBinding channelBinding)
- : base(settings, true, enableChannelBinding)
- {
- this.httpRequestMessage = httpRequestMessage;
- this.channelBinding = channelBinding;
- }
- public override long ContentLength
- {
- get
- {
- if (this.httpRequestMessage.Content.Headers.ContentLength == null)
- {
- // Chunked transfer mode
- return -1;
- }
- return this.httpRequestMessage.Content.Headers.ContentLength.Value;
- }
- }
- protected override ChannelBinding ChannelBinding
- {
- get
- {
- return this.channelBinding;
- }
- }
- public HttpRequestMessage HttpRequestMessage
- {
- get { return this.httpRequestMessage; }
- }
- protected override bool HasContent
- {
- get
- {
- // In Chunked transfer mode, the ContentLength header is null
- // Otherwise we just rely on the ContentLength header
- return this.httpRequestMessage.Content.Headers.ContentLength == null || this.httpRequestMessage.Content.Headers.ContentLength.Value > 0;
- }
- }
- protected override string ContentTypeCore
- {
- get
- {
- if (!this.HasContent)
- {
- return null;
- }
- return this.httpRequestMessage.Content.Headers.ContentType == null ? null : this.httpRequestMessage.Content.Headers.ContentType.MediaType;
- }
- }
- public override void ConfigureHttpRequestMessage(HttpRequestMessage message)
- {
- throw FxTrace.Exception.AsError(new InvalidOperationException());
- }
- protected override Stream GetInputStream()
- {
- if (this.httpRequestMessage.Content == null)
- {
- return Stream.Null;
- }
- return this.httpRequestMessage.Content.ReadAsStreamAsync().Result;
- }
- protected override void AddProperties(Message message)
- {
- HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(this.httpRequestMessage);
- message.Properties.Add(HttpRequestMessageProperty.Name, requestProperty);
- message.Properties.Via = this.httpRequestMessage.RequestUri;
- foreach (KeyValuePair<string, object> property in this.httpRequestMessage.Properties)
- {
- message.Properties.Add(property.Key, property.Value);
- }
- this.httpRequestMessage.Properties.Clear();
- }
- protected override string SoapActionHeader
- {
- get
- {
- IEnumerable<string> values;
- if (this.httpRequestMessage.Headers.TryGetValues(SoapAction, out values))
- {
- foreach (string headerValue in values)
- {
- return headerValue;
- }
- }
- return null;
- }
- }
- public void CopyHeaders(WebHeaderCollection headers)
- {
- // No special-casing for the "WWW-Authenticate" header required here,
- // because this method is only called for the incoming request
- // and the WWW-Authenticate header is a header only applied to responses.
- HttpChannelUtilities.CopyHeaders(this.httpRequestMessage, headers.Add);
- }
- internal void SetHttpRequestMessage(HttpRequestMessage httpRequestMessage)
- {
- Fx.Assert(httpRequestMessage != null, "httpRequestMessage should not be null.");
- this.httpRequestMessage = httpRequestMessage;
- }
- }
- }
|